﻿#include <windows.h>
#include <stdio.h>
#include <direct.h>
#include <io.h>
#include <atlconv.h>
#include "common.h"

//如果需要隐藏DOS窗口的话，就放开下面这句
//#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

#define D2TRUE TRUE
#define D2FALSE FALSE

typedef BOOL (* type_pfnCallback)();

enum VideoMode
{
    gdi = 1,
    software = 2,
    ddraw = 3,
    glide = 4,
    opengl = 5, //UNUSED
    d3d = 6,
    rave = 7 //UNUSED
};

enum GameRes
{
    res_640x480 = 0,
    res_800x600 = 2
};

enum GameMode
{
    none = 0x0,
    client = 0x1,
    server = 0x2,
    multiplayer = 0x3,
    launcher = 0x4,
    expand = 0x5
};

/*
下面的ST_CLIENT_DATA是1.13c/1.13d的初始化结构体定义，也是最新最完整的，我们这里用这个结构体来保存命令行参数
在后面，根据具体的游戏版本号，再重新分配并复制这个结构体的内容
不同版本之间结构体的对应关系，可以用winhex打开对应版本的game.exe，查看二进制代码，搜索NOSOUND字符串，就可以跳转到结构体的定义处，比如下面：
Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

0000CBB0            4E 4F 53 4F 55  4E 44 00 00 00 00 00 00      NOSOUND      
0000CBC0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 6E 73                 ns
0000CBD0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   
0000CBE0   00 00 00 00 00 00 00 00  00 00 00 00 1C 02 00 00                   
0000CBF0   00 00 00 00                                            


Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

0000C550                                        4E 4F 53 4F               NOSO
0000C560   55 4E 44 00 00 00 00 00  00 00 00 00 6E 73 00 00   UND         ns  
0000C570   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   
0000C580   1B 02 00 00 00 00 00 00                                    
上面是1.13c的game.exe，下面是1.12a的game.exe
从上面的对比可以看出，在1.13c里nosound这个选项的偏移为021C，而在1.12a里则是021B，据此就可以设定主要常用参数的偏移，从而定义出正确的结构体了
*/
//size must be 0x3C8
#pragma pack(push, 1)
typedef struct
{
    DWORD expansion;
    BYTE window_mode;
    BYTE fix_aspect_ratio;
    BYTE glide_mode;
    BYTE opengl_mode;
    BYTE rave_mode;
    BYTE d3d_mode;
    BYTE perspective;
    BYTE low_quality;
    DWORD gamma;
    BYTE vsync;
    DWORD frame_rate;
    char acPadding6[4];
    WORD join_id;
    char game_name[24];
    char game_ip[24];
    char bnet_ip[24];
    char mcp_ip[24];
    DWORD unk1;
    BYTE no_pk;
    BYTE open_c;
    BYTE amazon;
    BYTE paladin;
    BYTE sorceress;
    BYTE necromancer;
    BYTE barbarian;
    BYTE dru;
    BYTE asn;
    BYTE invincible;
    char account_name[48];
    char player_name[24];
    char realm_name[27];
    char acPadding[0xFD];
    WORD c_temp;
    WORD char_flags;
    BYTE no_monsters;
    DWORD monster_class;
    BYTE monster_info;
    DWORD monster_debug;
    BYTE item_rare;
    BYTE item_unique;
    BYTE bPadding[2];
    DWORD act;
    BYTE no_preload;
    BYTE direct;
    BYTE low_end;
    BYTE no_gfx_compress;
    DWORD arena;
    char acPadding2[6];
    type_pfnCallback mpq_callback;
    BYTE txt;
    BYTE log;
    BYTE msg_log;
    BYTE safe_mode;
    BYTE no_save;
    DWORD seed;
    BYTE cheats;
    BYTE teen;
    BYTE no_sound;
    BYTE quests;
    BYTE unk4;
    BYTE build;
    BYTE sound_background;
    void* bnet_callbacks;
    char acPadding3[0x1C];
    char game_pass[24];
    char acPadding4[0x100];
    BYTE skip_to_bnet;
    BYTE unk5;
    char acPadding5[0x6B];
    WORD unk6;
} ST_CLIENT_DATA;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct
{
    DWORD expansion;
    BYTE window_mode;
    BYTE glide_mode;
    BYTE opengl_mode;
    BYTE rave_mode;
    BYTE d3d_mode;
    BYTE perspective;
    BYTE low_quality;
    DWORD gamma;
    BYTE vsync;
    DWORD frame_rate;
    char acPadding6[4];
    WORD join_id;
    char game_name[24];
    char game_ip[24];
    char bnet_ip[24];
    char mcp_ip[24];
    DWORD unk1;
    BYTE no_pk;
    BYTE open_c;
    BYTE amazon;
    BYTE paladin;
    BYTE sorceress;
    BYTE necromancer;
    BYTE barbarian;
    BYTE dru;
    BYTE asn;
    BYTE invincible;
    char account_name[48];
    char player_name[24];
    char realm_name[27];
    char acPadding[0xFD];
    WORD c_temp;
    WORD char_flags;
    BYTE no_monsters;
    DWORD monster_class;
    BYTE monster_info;
    DWORD monster_debug;
    BYTE item_rare;
    BYTE item_unique;
    BYTE bPadding[2];
    DWORD act;
    BYTE no_preload;
    BYTE direct;
    BYTE low_end;
    BYTE no_gfx_compress;
    DWORD arena;
    char acPadding2[6];
    type_pfnCallback mpq_callback;
    BYTE txt;
    BYTE log;
    BYTE msg_log;
    BYTE safe_mode;
    BYTE no_save;
    DWORD seed;
    BYTE cheats;
    BYTE teen;
    BYTE no_sound;
    BYTE quests;
    BYTE unk4;
    BYTE build;
    void* bnet_callbacks;
    char acPadding3[0x1C];
    char game_pass[24];
    char acPadding4[0x100];
    BYTE skip_to_bnet;
    BYTE unk5;
    char acPadding5[0x6B];
    WORD unk6;
} ST_CLIENT_DATA_112;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct
{
    DWORD expansion;
    BYTE window_mode;
    BYTE glide_mode;
    BYTE opengl_mode;
    BYTE rave_mode;
    BYTE d3d_mode;
    BYTE perspective;
    BYTE low_quality;
    DWORD gamma;
    BYTE vsync;
    DWORD frame_rate;
    char acPadding6[4];
    WORD join_id;
    char game_name[24];
    char game_ip[24];
    char bnet_ip[24];
    char mcp_ip[24];
    DWORD unk1;
    BYTE no_pk;
    BYTE open_c;
    BYTE amazon;
    BYTE paladin;
    BYTE sorceress;
    BYTE necromancer;
    BYTE barbarian;
    BYTE dru;
    BYTE asn;
    BYTE invincible;
    char account_name[48];
    char player_name[24];
    char realm_name[27];
    char acPadding[0xFD];
    WORD c_temp;
    WORD char_flags;
    BYTE no_monsters;
    DWORD monster_class;
    BYTE monster_info;
    DWORD monster_debug;
    BYTE item_rare;
    BYTE item_unique;
    BYTE bPadding[2];
    DWORD act;
    BYTE no_preload;
    BYTE direct;
    BYTE low_end;
    BYTE no_gfx_compress;
    DWORD arena;
    char acPadding2[6];
    type_pfnCallback mpq_callback;
    BYTE txt;
    BYTE log;
    BYTE msg_log;
    BYTE safe_mode;
    BYTE no_save;
    DWORD seed;
    BYTE cheats;
    BYTE teen;
    BYTE no_sound;
    BYTE quests;
    BYTE unk4;
    BYTE build;
    void* bnet_callbacks;
    char acPadding3[0x1C];
    char game_pass[24];
    char acPadding4[0x100];
    BYTE skip_to_bnet;
    BYTE unk5;
    char acPadding5[0x6B];
    WORD unk6;
} ST_CLIENT_DATA_111;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct
{
    DWORD expansion;
    BYTE window_mode;
    BYTE glide_mode;
    BYTE opengl_mode;
    BYTE rave_mode;
    BYTE d3d_mode;
    BYTE perspective;
    BYTE low_quality;
    DWORD gamma;
    BYTE vsync;
    DWORD frame_rate;
    char acPadding6[4];
    WORD join_id;
    char game_name[24];
    char game_ip[24];
    char bnet_ip[24];
    char mcp_ip[24];
    DWORD unk1;
    BYTE no_pk;
    BYTE open_c;
    BYTE amazon;
    BYTE paladin;
    BYTE sorceress;
    BYTE necromancer;
    BYTE barbarian;
    BYTE dru;
    BYTE asn;
    BYTE invincible;
    char account_name[48];
    char player_name[24];
    char realm_name[27];
    char acPadding[0xFD];
    WORD c_temp;
    WORD char_flags;
    BYTE no_monsters;
    DWORD monster_class;
    BYTE monster_info;
    DWORD monster_debug;
    BYTE item_rare;
    BYTE item_unique;
    BYTE bPadding[2];
    DWORD act;
    BYTE no_preload;
    BYTE direct;
    BYTE low_end;
    BYTE no_gfx_compress;
    DWORD arena;
    char acPadding2[6];
    type_pfnCallback mpq_callback;
    BYTE txt;
    BYTE log;
    BYTE msg_log;
    BYTE safe_mode;
    BYTE no_save;
    DWORD seed;
    BYTE cheats;
    BYTE teen;
    BYTE no_sound;
    BYTE quests;
    BYTE unk4;
    BYTE build;
    void* bnet_callbacks;
    char acPadding3[0x1C];
    char game_pass[24];
    char acPadding4[0x100];
    BYTE skip_to_bnet;
    BYTE unk5;
    char acPadding5[0x6B];
    WORD unk6;
} ST_CLIENT_DATA_110;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct
{
    DWORD expansion;
    BYTE window_mode;
    BYTE glide_mode;
    BYTE opengl_mode;
    BYTE rave_mode;
    BYTE d3d_mode;
    BYTE perspective;
    BYTE low_quality;
    DWORD gamma;
    BYTE vsync;
    DWORD frame_rate;
    char acPadding6[4];
    WORD join_id;
    char game_name[24];
    char game_ip[24];
    char bnet_ip[24];
    char mcp_ip[24];
    DWORD unk1;
    BYTE no_pk;
    BYTE open_c;
    BYTE amazon;
    BYTE paladin;
    BYTE sorceress;
    BYTE necromancer;
    BYTE barbarian;
    BYTE dru;
    BYTE asn;
    BYTE invincible;
    char account_name[48];
    char player_name[24];
    char realm_name[27];
    char acPadding[0xFD];
    WORD c_temp;
    WORD char_flags;
    BYTE no_monsters;
    DWORD monster_class;
    BYTE monster_info;
    DWORD monster_debug;
    BYTE item_rare;
    BYTE item_unique;
    BYTE bPadding[2];
    DWORD act;
    BYTE no_preload;
    BYTE direct;
    BYTE low_end;
    BYTE no_gfx_compress;
    DWORD arena;
    char acPadding2[6];
    type_pfnCallback mpq_callback;
    BYTE txt;
    BYTE log;
    BYTE msg_log;
    BYTE safe_mode;
    BYTE no_save;
    DWORD seed;
    BYTE cheats;
    BYTE no_sound;
    BYTE quests;
    BYTE unk4;
    void* bnet_callbacks;
    char acPadding3[0x1C];
    char game_pass[24];
    char acPadding4[0x100];
    BYTE skip_to_bnet;
    BYTE unk5;
    char acPadding5[0x6B];
    WORD unk6;
} ST_CLIENT_DATA_109;
#pragma pack(pop)

typedef DWORD (__stdcall *type_pfnStormRegLoadString)(const char* keyname, const char* valuename, int a3, char* buffer, size_t buffersize);
typedef DWORD (__stdcall *type_pfnStormRegLoadValue)(const char* keyname, const char* valuename, int a3, int* value);
typedef DWORD (__stdcall *type_pfnStormOpenArchive)(LPCSTR lpFileName, DWORD dwPriority, DWORD dwFlags, HANDLE* hMPQ);
typedef DWORD (__stdcall *type_pfnStormCloseArchive)(HANDLE hMPQ);
typedef int (__fastcall* type_pfnStormSprintf)(void*, DWORD, const char*, ...);
typedef void (__fastcall *type_pfnFogSetLogPrefix)(const char* szPrefix);
typedef void (__fastcall *type_pfnFogSetErrorHandler)(const char* szApp, void* fnCallback, const char* szVersion, BOOL bAddInfo);
typedef void (__fastcall *type_pfnFogSetFileOptions)(BOOL bDirect, BOOL bQuickSeek);
typedef void (__fastcall *type_pfnFogSetAsyncData)(BOOL bState, DWORD dwUnknown);
typedef void (__fastcall *type_pfnFogFreeAsyncData)();
typedef BOOL (__fastcall *type_pfnIsErrorState)();
typedef void (__fastcall *type_pfnFogInit)();
typedef BOOL (__fastcall *type_pfnIsErrorState)();
typedef void (__fastcall *type_pfnErrorRescue)();
typedef void (__stdcall *type_pfnFreePools)(void* pParams);
typedef void (__stdcall *type_pfnSetServerParams)(void* pParams);
typedef BOOL(__fastcall *type_pfnFogIsExpansion)();
typedef BOOL (__stdcall *type_pfnD2WinLoadMPQs)();
typedef BOOL (__fastcall *type_pfnD2WinLoadExpansionMPQs)(int*, int*, BOOL, void*);
typedef void (__stdcall *type_pfnD2WinUnloadMPQs)();
typedef BOOL (__stdcall *type_pfnD2WinInitGfx)(HINSTANCE hInstance, int nDriver, BOOL bWindowed, BOOL bGFXCompress);
typedef BOOL (__stdcall *type_pfnD2WinDeinitGFX)();
typedef BOOL (__stdcall *type_pfnD2WinCreateWindow)(BOOL bWindowed, int ResoulutionMode);
typedef DWORD (__fastcall *type_pfnD2LaunchCall)(void *pvParam);
typedef void* (__stdcall *type_pfnD2GetHwnd)();
typedef void (__stdcall *type_pfnD2GfxRelease)();
typedef DWORD (__stdcall *type_pfnD2LangGetUsedLanguage)();
typedef void (__fastcall *type_pfnD2SoundInit)(BOOL bExpansion, BOOL bSoundBackground);
typedef DWORD (__fastcall *type_pfnD2Common10097)();
typedef void (__fastcall *type_pfnD2Fog10082)();
typedef void (__stdcall *type_pfnD2gfx10068)(DWORD);
typedef void (__stdcall *type_pfnD2gfx10071)(DWORD);
typedef int (__stdcall *type_pfnStorm426)(const char *, const char *, DWORD, DWORD);
typedef HWND (__stdcall *type_pfnGetHwnd)();

static type_pfnStormRegLoadString m_pfnStormRegLoadString = NULL;
static type_pfnStormRegLoadValue m_pfnStormRegLoadValue = NULL;
static type_pfnStormOpenArchive m_pfnStormOpenArchive = NULL;
static type_pfnStormCloseArchive m_pfnStormCloseArchive = NULL;
static type_pfnStormSprintf m_pfnStormSprintf = NULL;
static type_pfnFogSetLogPrefix m_pfnFogSetLogPrefix = NULL;
static type_pfnFogSetErrorHandler m_pfnFogSetErrorHandler = NULL;
static type_pfnFogSetFileOptions m_pfnFogSetFileOptions = NULL;
static type_pfnFogSetAsyncData m_pfnFogSetAsyncData = NULL;
static type_pfnFogInit m_pfnFogInit = NULL;
static type_pfnFogIsExpansion m_pfnFogIsExpansion = NULL;
static type_pfnIsErrorState m_pfnIsErrorState = NULL;
static type_pfnErrorRescue m_pfnErrorRescue = NULL;
static type_pfnFogFreeAsyncData m_pfnFogFreeAsyncData = NULL;
static type_pfnFreePools m_pfnFreePools = NULL;
static type_pfnSetServerParams m_pfnSetServerParams = NULL;
static type_pfnD2WinLoadMPQs m_pfnD2WinLoadMPQs = NULL;
static type_pfnD2WinLoadExpansionMPQs m_pfnD2WinLoadExpansionMPQs = NULL;
static type_pfnD2WinUnloadMPQs m_pfnD2WinUnloadMPQs = NULL;
static type_pfnD2WinInitGfx m_pfnD2WinInitGfx = NULL;
static type_pfnD2WinDeinitGFX m_pfnD2WinDeinitGFX = NULL;
static type_pfnD2WinCreateWindow m_pfnD2WinCreateWindow = NULL;
static type_pfnD2GetHwnd m_pfnD2GetHwnd = NULL;
static type_pfnD2GfxRelease m_pfnD2GfxRelease = NULL;
static type_pfnD2GfxRelease m_pfnD2LangFree = NULL;
static type_pfnD2LangGetUsedLanguage m_pfnD2LangGetUsedLanguage = NULL;
static type_pfnD2SoundInit m_pfnD2SoundInit = NULL;
static type_pfnD2GfxRelease m_pfnD2SoundShutdown = NULL;
static type_pfnD2Common10097 m_pfnD2Common10097 = NULL;
static type_pfnD2Fog10082 m_pfnD2Fog10082 = NULL;
static type_pfnD2gfx10068 m_pfnD2GfxSetPerspective = NULL;
static type_pfnD2Fog10082 m_pfnD2GfxSetLowQuality = NULL;
static type_pfnD2gfx10071 m_pfnD2GfxSetGamma = NULL;
static type_pfnD2Fog10082 m_pfnD2GfxFixAspectRatio = NULL;
static type_pfnStorm426 m_pfnStormSetResolution = NULL;
static type_pfnD2Fog10082 m_pfnD2WinUninit = NULL;
static type_pfnD2Fog10082 m_pfnD2MCPClientUninit = NULL;
static type_pfnD2Fog10082 m_pfnD2Common10925 = NULL;
static type_pfnGetHwnd m_pfnGetHwnd = NULL;

typedef struct
{
    type_pfnD2LaunchCall pfnLaunch;
    DWORD dwUnknown;
} ST_LAUNCH_CALLBACK;

typedef ST_LAUNCH_CALLBACK* (__fastcall *type_pfnD2LaunchGetCb)();
static type_pfnD2LaunchGetCb m_pfnD2LaunchGetCb = NULL;
static type_pfnD2LaunchGetCb m_pfnD2ClientGetCb = NULL;
static type_pfnD2LaunchGetCb m_pfnD2MultiGetCb = NULL;

static const char* m_pcBoxName = "D2Loader";
static char m_acLanguageMpq[MAX_PATH] = {0};
static char m_acGameTitle[MAX_PATH] = {0};
#define MAX_EXTEND_MPQ  10
static char m_aacExtendMpq[MAX_EXTEND_MPQ][MAX_PATH] = {0};
static DWORD m_dwExtendMpq = 0;
#define MAX_EXTEND_PLUGIN  10
static char m_aacExtendPlugin[MAX_EXTEND_PLUGIN][MAX_PATH] = {0};
static DWORD m_dwExtendPlugin = 0;
static char m_aacGlobalMpqPath[MAX_EXTEND_MPQ][MAX_PATH] = {0};
static DWORD m_dwGlobalMpqPath = 0;
static BOOL m_boolDepFix = FALSE;
static BOOL m_boolNoBorder = FALSE;
static BOOL m_boolMultiOpen = FALSE;
static BOOL m_boolCheckStruct = FALSE;

static void D2Critical_Callback()
{
    msgBox(m_pcBoxName, MB_OK | MB_ICONASTERISK, "An critical error occured!");
    exit(1);
}

static BOOL Proc_new()
{
    return D2TRUE;
}

typedef void (__stdcall *type_pfnDllInit)();
static DWORD D2Loader_LoadLibrary(const char *dll)
{
    type_pfnDllInit pfnDllInit = NULL;
    char acDllName[MAX_PATH] = {0};
    const char *pcTemp = strchr(dll, ':');

    if ( NULL != pcTemp )
    {
        memcpy(acDllName, dll, (DWORD)(pcTemp - dll));
        pcTemp++;
    }
    else
    {
        strcpy_s(acDllName, sizeof(acDllName), dll);
    }

    DWORD module = (DWORD)GetModuleHandle(acDllName);
    if ( NULL != module )
    {
        return module;
    }

    module = (DWORD)LoadLibraryEx(acDllName, NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
    if ( NULL == module )
    {
        msgBox(m_pcBoxName, MB_OK | MB_ICONASTERISK, "Load %s failed!", acDllName);
        exit(1);
    }

    if ( NULL != pcTemp && NULL != (pfnDllInit = (type_pfnDllInit)GetProcAddress((HMODULE)module, pcTemp)) )
    {
        pfnDllInit();
    }

    return module;
}

static DWORD GetDllOffset2(const char* dll, const char* pcName)
{
    HMODULE module = (HMODULE)D2Loader_LoadLibrary(dll);
    DWORD locatedAddress = (DWORD)GetProcAddress(module, (LPCSTR)pcName);
    return locatedAddress;
}

static DWORD GetDllOffset(const char* dll, int offset)
{
    return GetDllOffset2(dll, (LPCSTR)offset);
}

static void LoadAllLibraries()
{
    const char* apcLibrary[] = {"Storm.dll", "Fog.dll", "D2Sound.dll", "D2Lang.dll", "D2Gfx.dll", "D2Win.dll", "D2Launch.dll", "Bnclient.dll"};

    for ( int i = 0; i < sizeof(apcLibrary) / sizeof(apcLibrary[0]); i++ )
    {
        D2Loader_LoadLibrary(apcLibrary[i]);
    }
}

static int m_iGameVersion = -1;
static BOOL D2Loader_InitFuncPtr()
{
    switch ( m_iGameVersion )
    {
        case V109d:
            m_pfnStormRegLoadString = (type_pfnStormRegLoadString)GetDllOffset("Storm.dll", 422);
            m_pfnStormSprintf = (type_pfnStormSprintf)GetDllOffset("Storm.dll", 578);
            m_pfnFogSetLogPrefix = (type_pfnFogSetLogPrefix)GetDllOffset("Fog.dll", 10021);
            m_pfnFogSetErrorHandler = (type_pfnFogSetErrorHandler)GetDllOffset("Fog.dll", 10019);
            m_pfnFogSetFileOptions = (type_pfnFogSetFileOptions)GetDllOffset("Fog.dll", 10101);
            m_pfnFogSetAsyncData = (type_pfnFogSetAsyncData)GetDllOffset("Fog.dll", 10089);
            m_pfnFogInit = (type_pfnFogInit)GetDllOffset("Fog.dll", 10218);
            m_pfnD2WinLoadMPQs = (type_pfnD2WinLoadMPQs)GetDllOffset("D2Win.dll", 10037);
            m_pfnD2WinLoadExpansionMPQs = (type_pfnD2WinLoadExpansionMPQs)GetDllOffset("D2Win.dll", 10171);
            m_pfnFogIsExpansion = (type_pfnFogIsExpansion)GetDllOffset("Fog.dll", 10227);
            m_pfnD2WinInitGfx = (type_pfnD2WinInitGfx)GetDllOffset("D2Win.dll", 10000);
            m_pfnD2WinCreateWindow = (type_pfnD2WinCreateWindow)GetDllOffset("D2Win.dll", 10001);
            m_pfnD2SoundInit = (type_pfnD2SoundInit)GetDllOffset("D2Sound.dll", 10000);
            m_pfnD2SoundShutdown = (type_pfnD2GfxRelease)GetDllOffset("D2Sound.dll", 10001);
            m_pfnD2WinDeinitGFX = (type_pfnD2WinDeinitGFX)GetDllOffset("D2Win.dll", 10002);
            m_pfnD2GfxRelease = (type_pfnD2GfxRelease)GetDllOffset("D2Gfx.dll", 10001);
            m_pfnD2GetHwnd = (type_pfnD2GetHwnd)GetDllOffset("D2Gfx.dll", 10027);
            m_pfnStormRegLoadValue = (type_pfnStormRegLoadValue)GetDllOffset("Storm.dll", 423);
            m_pfnStormOpenArchive = (type_pfnStormOpenArchive)GetDllOffset("Storm.dll", 266);
            m_pfnStormCloseArchive = (type_pfnStormCloseArchive)GetDllOffset("Storm.dll", 252);
            m_pfnFogFreeAsyncData = (type_pfnFogFreeAsyncData)GetDllOffset("Fog.dll", 10090);
            m_pfnFreePools = (type_pfnFreePools)GetDllOffset("Fog.dll", 10143);
            m_pfnSetServerParams = (type_pfnSetServerParams)GetDllOffset("Fog.dll", 10185);
            m_pfnD2Fog10082 = (type_pfnD2Fog10082)GetDllOffset("Fog.dll", 10082);
            m_pfnD2GfxSetPerspective = (type_pfnD2gfx10068)GetDllOffset("D2Gfx.dll", 10011);
            m_pfnD2GfxSetLowQuality = (type_pfnD2Fog10082)GetDllOffset("D2Gfx.dll", 10015);
            m_pfnD2GfxSetGamma = (type_pfnD2gfx10071)GetDllOffset("D2Gfx.dll", 10018);
            m_pfnStormSetResolution = (type_pfnStorm426)GetDllOffset("Storm.dll", 426);
            m_pfnD2WinUninit = (type_pfnD2Fog10082)GetDllOffset("D2Win.dll", 10036);
            m_pfnD2MCPClientUninit = (type_pfnD2Fog10082)GetDllOffset("D2MCPClient.dll", 10001);
            m_pfnGetHwnd = (type_pfnGetHwnd)GetDllOffset("D2Gfx.dll", 10027);
            break;

        case V110f:
            m_pfnStormRegLoadString = (type_pfnStormRegLoadString)GetDllOffset("Storm.dll", 422);
            m_pfnStormSprintf = (type_pfnStormSprintf)GetDllOffset("Storm.dll", 578);
            m_pfnFogSetLogPrefix = (type_pfnFogSetLogPrefix)GetDllOffset("Fog.dll", 10021);
            m_pfnFogSetErrorHandler = (type_pfnFogSetErrorHandler)GetDllOffset("Fog.dll", 10019);
            m_pfnFogSetFileOptions = (type_pfnFogSetFileOptions)GetDllOffset("Fog.dll", 10101);
            m_pfnFogSetAsyncData = (type_pfnFogSetAsyncData)GetDllOffset("Fog.dll", 10089);
            m_pfnFogInit = (type_pfnFogInit)GetDllOffset("Fog.dll", 10218);
            m_pfnD2WinLoadMPQs = (type_pfnD2WinLoadMPQs)GetDllOffset("D2Win.dll", 10037);
            m_pfnD2WinLoadExpansionMPQs = (type_pfnD2WinLoadExpansionMPQs)GetDllOffset("D2Win.dll", 10171);
            m_pfnFogIsExpansion = (type_pfnFogIsExpansion)GetDllOffset("Fog.dll", 10227);
            m_pfnD2WinInitGfx = (type_pfnD2WinInitGfx)GetDllOffset("D2Win.dll", 10000);
            m_pfnD2WinCreateWindow = (type_pfnD2WinCreateWindow)GetDllOffset("D2Win.dll", 10001);
            m_pfnD2SoundInit = (type_pfnD2SoundInit)GetDllOffset("D2Sound.dll", 10000);
            m_pfnD2SoundShutdown = (type_pfnD2GfxRelease)GetDllOffset("D2Sound.dll", 10001);
            m_pfnD2WinDeinitGFX = (type_pfnD2WinDeinitGFX)GetDllOffset("D2Win.dll", 10002);
            m_pfnD2GfxRelease = (type_pfnD2GfxRelease)GetDllOffset("D2Gfx.dll", 10001);
            m_pfnD2GetHwnd = (type_pfnD2GetHwnd)GetDllOffset("D2Gfx.dll", 10027);
            m_pfnStormRegLoadValue = (type_pfnStormRegLoadValue)GetDllOffset("Storm.dll", 423);
            m_pfnStormOpenArchive = (type_pfnStormOpenArchive)GetDllOffset("Storm.dll", 266);
            m_pfnStormCloseArchive = (type_pfnStormCloseArchive)GetDllOffset("Storm.dll", 252);
            m_pfnFogFreeAsyncData = (type_pfnFogFreeAsyncData)GetDllOffset("Fog.dll", 10090);
            m_pfnFreePools = (type_pfnFreePools)GetDllOffset("Fog.dll", 10143);
            m_pfnSetServerParams = (type_pfnSetServerParams)GetDllOffset("Fog.dll", 10185);
            m_pfnD2Fog10082 = (type_pfnD2Fog10082)GetDllOffset("Fog.dll", 10082);
            m_pfnD2GfxSetPerspective = (type_pfnD2gfx10068)GetDllOffset("D2Gfx.dll", 10011);
            m_pfnD2GfxSetLowQuality = (type_pfnD2Fog10082)GetDllOffset("D2Gfx.dll", 10015);
            m_pfnD2GfxSetGamma = (type_pfnD2gfx10071)GetDllOffset("D2Gfx.dll", 10018);
            m_pfnStormSetResolution = (type_pfnStorm426)GetDllOffset("Storm.dll", 426);
            m_pfnD2WinUninit = (type_pfnD2Fog10082)GetDllOffset("D2Win.dll", 10036);
            m_pfnD2MCPClientUninit = (type_pfnD2Fog10082)GetDllOffset("D2MCPClient.dll", 10001);
            m_pfnGetHwnd = (type_pfnGetHwnd)GetDllOffset("D2Gfx.dll", 10027);
            break;

        case V111b:
            m_pfnStormRegLoadString = (type_pfnStormRegLoadString)GetDllOffset("Storm.dll", 422);
            m_pfnStormSprintf = (type_pfnStormSprintf)GetDllOffset("Storm.dll", 578);
            m_pfnFogSetLogPrefix = (type_pfnFogSetLogPrefix)GetDllOffset("Fog.dll", 10021);
            m_pfnFogSetErrorHandler = (type_pfnFogSetErrorHandler)GetDllOffset("Fog.dll", 10019);
            m_pfnFogSetFileOptions = (type_pfnFogSetFileOptions)GetDllOffset("Fog.dll", 10101);
            m_pfnFogSetAsyncData = (type_pfnFogSetAsyncData)GetDllOffset("Fog.dll", 10089);
            m_pfnFogInit = (type_pfnFogInit)GetDllOffset("Fog.dll", 10218);
            m_pfnD2WinLoadMPQs = (type_pfnD2WinLoadMPQs)GetDllOffset("D2Win.dll", 10030);
            m_pfnD2WinLoadExpansionMPQs = (type_pfnD2WinLoadExpansionMPQs)GetDllOffset("D2Win.dll", 10051);
            m_pfnFogIsExpansion = (type_pfnFogIsExpansion)GetDllOffset("Fog.dll", 10227);
            m_pfnD2WinInitGfx = (type_pfnD2WinInitGfx)GetDllOffset("D2Win.dll", 10089);
            m_pfnD2WinCreateWindow = (type_pfnD2WinCreateWindow)GetDllOffset("D2Win.dll", 10100);
            m_pfnD2SoundInit = (type_pfnD2SoundInit)GetDllOffset("D2Sound.dll", 10050);
            m_pfnD2SoundShutdown = (type_pfnD2GfxRelease)GetDllOffset("D2Sound.dll", 10044);
            m_pfnD2WinDeinitGFX = (type_pfnD2WinDeinitGFX)GetDllOffset("D2Win.dll", 10182);
            m_pfnD2GfxRelease = (type_pfnD2GfxRelease)GetDllOffset("D2Gfx.dll", 10034);
            m_pfnD2WinUnloadMPQs = (type_pfnD2WinUnloadMPQs)GetDllOffset("D2Win.dll", 10177);
            m_pfnD2GetHwnd = (type_pfnD2GetHwnd)GetDllOffset("D2Gfx.dll", 10026);
            m_pfnStormRegLoadValue = (type_pfnStormRegLoadValue)GetDllOffset("Storm.dll", 423);
            m_pfnStormOpenArchive = (type_pfnStormOpenArchive)GetDllOffset("Storm.dll", 266);
            m_pfnStormCloseArchive = (type_pfnStormCloseArchive)GetDllOffset("Storm.dll", 252);
            m_pfnFogFreeAsyncData = (type_pfnFogFreeAsyncData)GetDllOffset("Fog.dll", 10090);
            m_pfnFreePools = (type_pfnFreePools)GetDllOffset("Fog.dll", 10143);
            m_pfnD2Fog10082 = (type_pfnD2Fog10082)GetDllOffset("Fog.dll", 10082);
            m_pfnD2GfxSetPerspective = (type_pfnD2gfx10068)GetDllOffset("D2Gfx.dll", 10061);
            m_pfnD2GfxSetLowQuality = (type_pfnD2Fog10082)GetDllOffset("D2Gfx.dll", 10055);
            m_pfnD2GfxSetGamma = (type_pfnD2gfx10071)GetDllOffset("D2Gfx.dll", 10003);
            m_pfnStormSetResolution = (type_pfnStorm426)GetDllOffset("Storm.dll", 426);
            m_pfnD2WinUninit = (type_pfnD2Fog10082)GetDllOffset("D2Win.dll", 10032);
            m_pfnD2MCPClientUninit = (type_pfnD2Fog10082)GetDllOffset("D2MCPClient.dll", 10039);
            m_pfnGetHwnd = (type_pfnGetHwnd)GetDllOffset("D2Gfx.dll", 10022);
            break;

        case V112a:
            m_pfnStormRegLoadString = (type_pfnStormRegLoadString)GetDllOffset("Storm.dll", 422);
            m_pfnStormSprintf = (type_pfnStormSprintf)GetDllOffset("Storm.dll", 578);
            m_pfnFogSetLogPrefix = (type_pfnFogSetLogPrefix)GetDllOffset("Fog.dll", 10021);
            m_pfnFogSetErrorHandler = (type_pfnFogSetErrorHandler)GetDllOffset("Fog.dll", 10019);
            m_pfnFogSetFileOptions = (type_pfnFogSetFileOptions)GetDllOffset("Fog.dll", 10101);
            m_pfnFogSetAsyncData = (type_pfnFogSetAsyncData)GetDllOffset("Fog.dll", 10089);
            m_pfnFogInit = (type_pfnFogInit)GetDllOffset("Fog.dll", 10218);
            m_pfnD2Fog10082 = (type_pfnD2Fog10082)GetDllOffset("Fog.dll", 10082);
            m_pfnD2WinLoadMPQs = (type_pfnD2WinLoadMPQs)GetDllOffset("D2Win.dll", 10059);
            m_pfnD2WinLoadExpansionMPQs = (type_pfnD2WinLoadExpansionMPQs)GetDllOffset("D2Win.dll", 10073);
            m_pfnFogIsExpansion = (type_pfnFogIsExpansion)GetDllOffset("Fog.dll", 10227);
            m_pfnStormOpenArchive = (type_pfnStormOpenArchive)GetDllOffset("Storm.dll", 266);
            m_pfnStormCloseArchive = (type_pfnStormCloseArchive)GetDllOffset("Storm.dll", 252);
            m_pfnD2WinInitGfx = (type_pfnD2WinInitGfx)GetDllOffset("D2Win.dll", 10188);
            m_pfnD2GfxSetPerspective = (type_pfnD2gfx10068)GetDllOffset("D2Gfx.dll", 10069);
            m_pfnD2WinCreateWindow = (type_pfnD2WinCreateWindow)GetDllOffset("D2Win.dll", 10109);
            m_pfnGetHwnd = (type_pfnGetHwnd)GetDllOffset("D2Gfx.dll", 10078);
            m_pfnD2GfxSetLowQuality = (type_pfnD2Fog10082)GetDllOffset("D2Gfx.dll", 10036);
            m_pfnD2GfxSetGamma = (type_pfnD2gfx10071)GetDllOffset("D2Gfx.dll", 10022);
            m_pfnStormRegLoadValue = (type_pfnStormRegLoadValue)GetDllOffset("Storm.dll", 423);
            m_pfnD2GetHwnd = (type_pfnD2GetHwnd)GetDllOffset("D2Gfx.dll", 10008);
            m_pfnStormSetResolution = (type_pfnStorm426)GetDllOffset("Storm.dll", 426);
            m_pfnD2SoundInit = (type_pfnD2SoundInit)GetDllOffset("D2Sound.dll", 10032);
            m_pfnD2SoundShutdown = (type_pfnD2GfxRelease)GetDllOffset("D2Sound.dll", 10002);
            m_pfnD2WinDeinitGFX = (type_pfnD2WinDeinitGFX)GetDllOffset("D2Win.dll", 10121);
            m_pfnD2GfxRelease = (type_pfnD2GfxRelease)GetDllOffset("D2Gfx.dll", 10010);
            m_pfnD2WinUninit = (type_pfnD2Fog10082)GetDllOffset("D2Win.dll", 10045);
            m_pfnFogFreeAsyncData = (type_pfnFogFreeAsyncData)GetDllOffset("Fog.dll", 10090);
            m_pfnD2MCPClientUninit = (type_pfnD2Fog10082)GetDllOffset("D2MCPClient.dll", 10024);
            m_pfnFreePools = (type_pfnFreePools)GetDllOffset("Fog.dll", 10143);
            m_pfnD2WinUnloadMPQs = (type_pfnD2WinUnloadMPQs)GetDllOffset("D2Win.dll", 10080);
            break;

        case V113c:
            m_pfnStormRegLoadString = (type_pfnStormRegLoadString)GetDllOffset("Storm.dll", 422);
            m_pfnStormSprintf = (type_pfnStormSprintf)GetDllOffset("Storm.dll", 578);
            m_pfnFogSetLogPrefix = (type_pfnFogSetLogPrefix)GetDllOffset("Fog.dll", 10021);
            m_pfnFogSetErrorHandler = (type_pfnFogSetErrorHandler)GetDllOffset("Fog.dll", 10019);
            m_pfnFogSetFileOptions = (type_pfnFogSetFileOptions)GetDllOffset("Fog.dll", 10101);
            m_pfnFogSetAsyncData = (type_pfnFogSetAsyncData)GetDllOffset("Fog.dll", 10089);
            m_pfnFogInit = (type_pfnFogInit)GetDllOffset("Fog.dll", 10218);
            m_pfnD2WinLoadMPQs = (type_pfnD2WinLoadMPQs)GetDllOffset("D2Win.dll", 10086);
            m_pfnD2WinLoadExpansionMPQs = (type_pfnD2WinLoadExpansionMPQs)GetDllOffset("D2Win.dll", 10005);
            m_pfnFogIsExpansion = (type_pfnFogIsExpansion)GetDllOffset("Fog.dll", 10227);
            m_pfnD2WinInitGfx = (type_pfnD2WinInitGfx)GetDllOffset("D2Win.dll", 10142);
            m_pfnD2WinCreateWindow = (type_pfnD2WinCreateWindow)GetDllOffset("D2Win.dll", 10052);
            m_pfnD2SoundInit = (type_pfnD2SoundInit)GetDllOffset("D2Sound.dll", 10002);
            m_pfnD2SoundShutdown = (type_pfnD2GfxRelease)GetDllOffset("D2Sound.dll", 10031);
            m_pfnD2WinDeinitGFX = (type_pfnD2WinDeinitGFX)GetDllOffset("D2Win.dll", 10032);
            m_pfnD2GfxRelease = (type_pfnD2GfxRelease)GetDllOffset("D2Gfx.dll", 10084);
            m_pfnD2WinUnloadMPQs = (type_pfnD2WinUnloadMPQs)GetDllOffset("D2Win.dll", 10088);
            m_pfnD2GetHwnd = (type_pfnD2GetHwnd)GetDllOffset("D2Gfx.dll", 10007);
            m_pfnD2LangFree = (type_pfnD2GfxRelease)GetDllOffset("D2Lang.dll", 10000);
            m_pfnStormRegLoadValue = (type_pfnStormRegLoadValue)GetDllOffset("Storm.dll", 423);
            m_pfnStormOpenArchive = (type_pfnStormOpenArchive)GetDllOffset("Storm.dll", 266);
            m_pfnStormCloseArchive = (type_pfnStormCloseArchive)GetDllOffset("Storm.dll", 252);
            m_pfnIsErrorState = (type_pfnIsErrorState)GetDllOffset("Fog.dll", 10039);
            m_pfnErrorRescue = (type_pfnErrorRescue)GetDllOffset("Fog.dll", 10040);
            m_pfnFogFreeAsyncData = (type_pfnFogFreeAsyncData)GetDllOffset("Fog.dll", 10090);
            m_pfnFreePools = (type_pfnFreePools)GetDllOffset("Fog.dll", 10143);
            m_pfnSetServerParams = (type_pfnSetServerParams)GetDllOffset("Fog.dll", 10185);
            m_pfnD2Fog10082 = (type_pfnD2Fog10082)GetDllOffset("Fog.dll", 10082);
            m_pfnD2GfxSetPerspective = (type_pfnD2gfx10068)GetDllOffset("D2Gfx.dll", 10081);
            m_pfnD2GfxSetLowQuality = (type_pfnD2Fog10082)GetDllOffset("D2Gfx.dll", 10053);
            m_pfnD2GfxSetGamma = (type_pfnD2gfx10071)GetDllOffset("D2Gfx.dll", 10034);
            m_pfnD2GfxFixAspectRatio = (type_pfnD2Fog10082)GetDllOffset("D2Gfx.dll", 10066);
            m_pfnStormSetResolution = (type_pfnStorm426)GetDllOffset("Storm.dll", 426);
            m_pfnD2WinUninit = (type_pfnD2Fog10082)GetDllOffset("D2Win.dll", 10158);
            m_pfnD2MCPClientUninit = (type_pfnD2Fog10082)GetDllOffset("D2MCPClient.dll", 10018);
            m_pfnGetHwnd = (type_pfnGetHwnd)GetDllOffset("D2Gfx.dll", 10048);
            break;

        case V113d:            
            m_pfnStormRegLoadString = (type_pfnStormRegLoadString)GetDllOffset("Storm.dll", 422);
            m_pfnStormSprintf = (type_pfnStormSprintf)GetDllOffset("Storm.dll", 578);
            m_pfnFogSetLogPrefix = (type_pfnFogSetLogPrefix)GetDllOffset("Fog.dll", 10021);
            m_pfnFogSetErrorHandler = (type_pfnFogSetErrorHandler)GetDllOffset("Fog.dll", 10019);
            m_pfnFogSetFileOptions = (type_pfnFogSetFileOptions)GetDllOffset("Fog.dll", 10101);
            m_pfnFogSetAsyncData = (type_pfnFogSetAsyncData)GetDllOffset("Fog.dll", 10089);
            m_pfnFogInit = (type_pfnFogInit)GetDllOffset("Fog.dll", 10218);
            m_pfnD2WinLoadMPQs = (type_pfnD2WinLoadMPQs)GetDllOffset("D2Win.dll", 10174);
            m_pfnD2WinLoadExpansionMPQs = (type_pfnD2WinLoadExpansionMPQs)GetDllOffset("D2Win.dll", 10072);
            m_pfnFogIsExpansion = (type_pfnFogIsExpansion)GetDllOffset("Fog.dll", 10227);
            m_pfnD2WinInitGfx = (type_pfnD2WinInitGfx)GetDllOffset("D2Win.dll", 10071);
            m_pfnD2WinCreateWindow = (type_pfnD2WinCreateWindow)GetDllOffset("D2Win.dll", 10129);
            m_pfnD2SoundInit = (type_pfnD2SoundInit)GetDllOffset("D2Sound.dll", 10023);
            m_pfnD2SoundShutdown = (type_pfnD2GfxRelease)GetDllOffset("D2Sound.dll", 10024);
            m_pfnD2WinDeinitGFX = (type_pfnD2WinDeinitGFX)GetDllOffset("D2Win.dll", 10132);
            m_pfnD2GfxRelease = (type_pfnD2GfxRelease)GetDllOffset("D2Gfx.dll", 10050);
            m_pfnD2WinUnloadMPQs = (type_pfnD2WinUnloadMPQs)GetDllOffset("D2Win.dll", 10079);
            m_pfnD2GetHwnd = (type_pfnD2GetHwnd)GetDllOffset("D2Gfx.dll", 10007);
            m_pfnD2LangFree = (type_pfnD2GfxRelease)GetDllOffset("D2Lang.dll", 10000);
            m_pfnStormRegLoadValue = (type_pfnStormRegLoadValue)GetDllOffset("Storm.dll", 423);
            m_pfnStormOpenArchive = (type_pfnStormOpenArchive)GetDllOffset("Storm.dll", 266);
            m_pfnStormCloseArchive = (type_pfnStormCloseArchive)GetDllOffset("Storm.dll", 252);
            m_pfnIsErrorState = (type_pfnIsErrorState)GetDllOffset("Fog.dll", 10039);
            m_pfnErrorRescue = (type_pfnErrorRescue)GetDllOffset("Fog.dll", 10040);
            m_pfnFogFreeAsyncData = (type_pfnFogFreeAsyncData)GetDllOffset("Fog.dll", 10090);
            m_pfnFreePools = (type_pfnFreePools)GetDllOffset("Fog.dll", 10143);
            m_pfnSetServerParams = (type_pfnSetServerParams)GetDllOffset("Fog.dll", 10185);
            m_pfnD2Fog10082 = (type_pfnD2Fog10082)GetDllOffset("Fog.dll", 10082);
            m_pfnD2Common10097 = (type_pfnD2Common10097)GetDllOffset("D2Common.dll", 10097);
            m_pfnD2GfxSetPerspective = (type_pfnD2gfx10068)GetDllOffset("D2Gfx.dll", 10068);
            m_pfnD2GfxSetLowQuality = (type_pfnD2Fog10082)GetDllOffset("D2Gfx.dll", 10008);
            m_pfnD2GfxSetGamma = (type_pfnD2gfx10071)GetDllOffset("D2Gfx.dll", 10071);
            m_pfnD2GfxFixAspectRatio = (type_pfnD2Fog10082)GetDllOffset("D2Gfx.dll", 10049);
            m_pfnStormSetResolution = (type_pfnStorm426)GetDllOffset("Storm.dll", 426);
            m_pfnD2WinUninit = (type_pfnD2Fog10082)GetDllOffset("D2Win.dll", 10058);
            m_pfnD2MCPClientUninit = (type_pfnD2Fog10082)GetDllOffset("D2MCPClient.dll", 10006);
            m_pfnD2Common10925 = (type_pfnD2Fog10082)GetDllOffset("D2Common.dll", 10925);
            m_pfnGetHwnd = (type_pfnGetHwnd)GetDllOffset("D2Gfx.dll", 10007);
            break;
    
        default:
            break;
    }

    return TRUE;
}

static void parse_command(int argc, char** argv, ST_CLIENT_DATA *pstClientData)
{
    for ( int i = 1; i < argc; ++i )
    {
        if ( !_stricmp("-ver", argv[i]) )
        {
            m_iGameVersion = D2Loader_GetVersionId(argv[++i]);
        }
        else if ( !_stricmp("-check", argv[i]) )
        {
            m_boolCheckStruct = TRUE;
        }
        else if ( !_stricmp("-w", argv[i]) )
        {
            pstClientData->window_mode = D2TRUE;
        }
        else if ( !_stricmp("-glide", argv[i]) )
        {
            pstClientData->glide_mode = D2TRUE;
        }
        else if ( !_stricmp("-opengl", argv[i]) )
        {
            pstClientData->opengl_mode = D2TRUE;
        }
        else if ( !_stricmp("-d3d", argv[i]) )
        {
            pstClientData->d3d_mode = D2TRUE;
        }
        else if ( !_stricmp("-per", argv[i]) )
        {
            pstClientData->perspective = D2TRUE;
        }
        else if ( !_stricmp("-lq", argv[i]) )
        {
            pstClientData->low_quality = D2TRUE;
        }
        else if ( !_stricmp("-gamma", argv[i]) && i + 1 < argc )
        {
            pstClientData->gamma = atoi(argv[++i]);
        }
        else if ( !_stricmp("-vsync", argv[i]) )
        {
            pstClientData->vsync = D2TRUE;
        }
        else if ( !_stricmp("-fr", argv[i]) && i + 1 < argc )
        {
            pstClientData->frame_rate = atoi(argv[++i]);
        }
        else if ( !_stricmp("-joinid", argv[i]) && i + 1 < argc )
        {
            pstClientData->join_id = atoi(argv[++i]);
        }
        else if ( !_stricmp("-gamename", argv[i]) && i + 1 < argc )
        {
            strncpy_s(pstClientData->game_name, argv[++i], sizeof(pstClientData->game_name) - 1);
        }
        else if ( !_stricmp("-bn", argv[i]) && i + 1 < argc )
        {
            strncpy_s(pstClientData->bnet_ip, argv[++i], sizeof(pstClientData->bnet_ip) - 1);
        }
        else if ( !_stricmp("-mcpip", argv[i]) && i + 1 < argc )
        {
            strncpy_s(pstClientData->mcp_ip, argv[++i], sizeof(pstClientData->mcp_ip) - 1);
        }
        else if ( !_stricmp("-nopk", argv[i]) )
        {
            pstClientData->no_pk = D2TRUE;
        }
        else if ( !_stricmp("-openc", argv[i]) )
        {
            pstClientData->open_c = D2TRUE;
        }
        else if ( !_stricmp("-arena", argv[i]) )
        {
            pstClientData->arena = D2TRUE;
        }
        else if ( !_stricmp("-txt", argv[i]) )
        {
            pstClientData->txt = D2TRUE;
        }
        else if ( !_stricmp("-ama", argv[i]) )
        {
            pstClientData->amazon = D2TRUE;
        }
        else if ( !_stricmp("-pal", argv[i]) )
        {
            pstClientData->paladin = D2TRUE;
        }
        else if ( !_stricmp("-sor", argv[i]) )
        {
            pstClientData->sorceress = D2TRUE;
        }
        else if ( !_stricmp("-nec", argv[i]) )
        {
            pstClientData->necromancer = D2TRUE;
        }
        else if ( !_stricmp("-bar", argv[i]) )
        {
            pstClientData->barbarian = D2TRUE;
        }
        else if ( !_stricmp("-dru", argv[i]) )
        {
            pstClientData->dru = D2TRUE;
        }
        else if ( !_stricmp("-asn", argv[i]) )
        {
            pstClientData->asn = D2TRUE;
        }
        else if ( !_stricmp("-i", argv[i]) )
        {
            pstClientData->invincible = D2TRUE;
        }
        else if ( !_stricmp("-bnacct", argv[i]) && i + 1 < argc )
        {
            strncpy_s(pstClientData->account_name, argv[++i], sizeof(pstClientData->account_name) - 1);
        }
        else if ( !_stricmp("-bnpass", argv[i]) && i + 1 < argc )
        {
            strncpy_s(pstClientData->game_pass, argv[++i], sizeof(pstClientData->game_pass) - 1);
        }
        else if ( !_stricmp("-name", argv[i]) && i + 1 < argc )
        {
            strncpy_s(pstClientData->player_name, argv[++i], sizeof(pstClientData->player_name) - 1);
        }
        else if ( !_stricmp("-realm", argv[i]) && i + 1 < argc )
        {
            strncpy_s(pstClientData->realm_name, argv[++i], sizeof(pstClientData->realm_name) - 1);
        }
        else if ( !_stricmp("-ctemp", argv[i]) && i + 1 < argc )
        {
            pstClientData->c_temp = atoi(argv[++i]);
        }
        else if ( !_stricmp("-nm", argv[i]) )
        {
            pstClientData->no_monsters = D2TRUE;
        }
        else if ( !_stricmp("-m", argv[i]) )
        {
            pstClientData->monster_class = D2TRUE;
        }
        else if ( !_stricmp("-minfo", argv[i]) )
        {
            pstClientData->monster_info = D2TRUE;
        }
        else if ( !_stricmp("-md", argv[i]) )
        {
            pstClientData->monster_debug = D2TRUE;
        }
        else if ( !_stricmp("-rare", argv[i]) )
        {
            pstClientData->item_rare = D2TRUE;
        }
        else if ( !_stricmp("-unique", argv[i]) )
        {
            pstClientData->item_unique = D2TRUE;
        }
        else if ( !_stricmp("-act", argv[i]) && i + 1 < argc )
        {
            pstClientData->act = atoi(argv[++i]);
        }
        else if ( !_stricmp("-log", argv[i]) )
        {
            pstClientData->log = D2TRUE;
        }
        else if ( !_stricmp("-msglog", argv[i]) )
        {
            pstClientData->msg_log = D2TRUE;
        }
        else if ( !_stricmp("-safe", argv[i]) )
        {
            pstClientData->safe_mode = D2TRUE;
        }
        else if ( !_stricmp("-seed", argv[i]) && i + 1 < argc )
        {
            pstClientData->seed = atoi(argv[++i]);
        }
        else if ( !_stricmp("-cheats", argv[i]) )
        {
            pstClientData->cheats = D2TRUE;
        }
        else if ( !_stricmp("-ns", argv[i]) )
        {
            pstClientData->no_sound = D2TRUE;
        }
        else if ( !_stricmp("-questall", argv[i]) )
        {
            pstClientData->quests = D2TRUE;
        }
        else if ( !_stricmp("-npl", argv[i]) )
        {
            pstClientData->no_preload = D2TRUE;
        }
        else if ( !_stricmp("-direct", argv[i]) )
        {
            pstClientData->direct = D2TRUE;
        }
        else if ( !_stricmp("-lem", argv[i]) )
        {
            pstClientData->low_end = D2TRUE;
        }
        else if ( !_stricmp("-nocompress", argv[i]) )
        {
            pstClientData->no_gfx_compress = D2TRUE;
        }
        else if ( !_stricmp("-gamepass", argv[i]) && i + 1 < argc )
        {
            strncpy_s(pstClientData->game_pass, argv[++i], sizeof(pstClientData->game_pass) - 1);
        }
        else if ( !_stricmp("-skiptobnet", argv[i]) )
        {
            pstClientData->skip_to_bnet = D2TRUE;
        }
        else if ( !_stricmp("-client", argv[i]) )
        {
        }
        else if ( !_stricmp("-server", argv[i]) )
        {
        }
        else if ( !_stricmp("-launch", argv[i]) )
        {
        }
        else if ( !_stricmp("-notitle", argv[i]) )
        {
            strncpy_s(m_acGameTitle, "notitle", sizeof(m_acGameTitle) - 1);
        }
        else if ( !_stricmp("-res800", argv[i]) )
        {
        }
        else if ( !_stricmp("-res640", argv[i]) )
        {
        }
        else if ( !_stricmp("-title", argv[i]) && i + 1 < argc )
        {
            strncpy_s(m_acGameTitle, argv[++i], sizeof(m_acGameTitle) - 1);
        }
        else if ( !_stricmp("-locale", argv[i]) && i + 1 < argc )
        {
            sprintf_s(m_acLanguageMpq, "Language_%s\\%s.mpq", argv[i+1], argv[i+1]);
            ++i;
        }
        else if ( !_stricmp("-mpq", argv[i]) )
        {//指定额外的mpq，最多10个，空格分开，可以带路径，路径有空格要用双引号，比如：-mpq Language_CHI\CHI.mpq
            while ( i + 1 < argc && '-' != argv[i+1][0] && m_dwExtendMpq < MAX_EXTEND_MPQ )
            {
                sprintf_s(m_aacExtendMpq[m_dwExtendMpq++], "%s", argv[++i]);
            }
        }
        else if ( !_stricmp("-plugin", argv[i]) )
        {//指定额外的dll，最多10个，空格分开，可以带路径，路径有空格要用双引号，比如：-plugin d2hackmap\d2hackmap.dll
            //另外，可以追加plugin的初始化函数，比如：-plugin PlugY.dll:_Init@4
            while ( i + 1 < argc && '-' != argv[i+1][0] && m_dwExtendPlugin < MAX_EXTEND_PLUGIN )
            {
                sprintf_s(m_aacExtendPlugin[m_dwExtendPlugin++], "%s", argv[++i]);
            }
        }
        else if ( !_stricmp("-mpqpath", argv[i]) && i + 1 < argc )
        {//指定10 mpqs的加载路径，路径有空格要用双引号，路径的最后不要写反斜杠"\\"，比如：-mpqpath "d:\Diablo II D2SE"
            while ( i + 1 < argc && '-' != argv[i+1][0] && m_dwGlobalMpqPath < MAX_EXTEND_PLUGIN )
            {
                sprintf_s(m_aacGlobalMpqPath[m_dwGlobalMpqPath++], "%s", argv[++i]);
            }
        }
        else if ( !_stricmp("-dllpath", argv[i]) )
        {//追加dll的加载路径，路径有空格要用双引号，路径的最后不要写反斜杠"\\"，比如：-dllpath "d:\Diablo II D2SE" "d:\Diablo II D2SE\D2SE\CORES\1.13c"
            SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
            while ( i + 1 < argc && '-' != argv[i+1][0] )
            {
                USES_CONVERSION;
                AddDllDirectory(A2CW(argv[++i]));
            }
        }
        else if ( !_stricmp("-depfix", argv[i]) )
        {
            m_boolDepFix = TRUE;
        }
        else if ( !_stricmp("-noborder", argv[i]) )
        {
            m_boolNoBorder = TRUE;
        }
        else if ( !_stricmp("-multiopen", argv[i]) )
        {
            m_boolMultiOpen = TRUE;
        }
    }
    /*
    举两个例子：
    D2SE版1.13c
    D2Loader.exe -w -mpq language_chi\chi.mpq -mpqpath "d:\Diablo II D2SE" -dllpath "d:\Diablo II D2SE" "d:\Diablo II D2SE\D2se\CORES\1.13c" -plugin PlugY.dll:_Init@4 -glide -direct -txt
    或者
    D2Loader.exe -w -locale chi -mpqpath "d:\Diablo II D2SE" -dllpath "d:\Diablo II D2SE" "d:\Diablo II D2SE\D2se\CORES\1.13c" -plugin PlugY.dll:_Init@4 -glide -direct -txt
    D2SE版1.13d
    D2Loader.exe -w -mpqpath "d:\Diablo II D2SE" -direct -txt -plugin PlugY.dll:_Init@4 -glide
    */
    /*
    1.视频选项
    -exp -expansion 切换到扩展模式
    -w -window 切换到窗口模式
    -glide -glide 使用Glide显示模式
    -opengl -opengl 使用OpenGL显示模式
    -d3d -d3d 使用Direct 3D显示模式
    -rave -rave 使用Rave显示模式，仅适用于Mac
    -per -perspective 打开透视模式，仅适用于全屏非Direct Draw模式
    -lq -lowquality 低图像质量(高速度)
    -gamma -gamma 设置Gamma值为
    -vsync -vsync 打开VSync
    -fr -framerate 甚至帧速率为
    2.网络选项
    -s -serverip 设置TCP/IP游戏服务器的IP为
    -gametype -gametype 设置游戏类型为
    -joinid -joinid 设置加入游戏的ID为
    -gamename -gamename 设置游戏名为
    -bn -battlenetip 设置battle.net服务器IP为
    -mcpip -mcpip 设置mcpip服务器IP为
    -nopk -nopk 禁止PK(好像无效)
    -openc -openc 不清楚
    3.游戏选项
    -arena -arena 无效
    -difficulty -difficulty 无效
    -txt -txt 给MOD制作者，用于创建.bin文件
    4.角色选项
    -ama -ama 设置角色类型为ama
    -pal -pal 设置角色类型为pal
    -sor -sor 设置角色类型为sor
    -nec -nec 设置角色类型为nec
    -bar -bar 设置角色类型为bar
    -dru -dru 设置角色类型为dru
    -asn -asn 设置角色类型为asn
    -i -invincible 隐形？(好像无效)
    -bnacct -bnacct 设置battle.net账号名字为
    -bnpass -bnpass 设置battle.net密码为
    -name -name 设置battle.net角色名字为
    -realm -realm 设置battle.net服务器(Realm)名字为
    -ctemp -ctemp 在arena模式使用第个角色的模板
    5.怪物选项
    -nm -nomonster 无怪物？(无效)
    -m -monsterclass 不清楚
    -minfo -monsterinfo 显示怪物信息？(无效)
    -md -monsterdebug 不清楚
    6.物品选项
    -rare -rare 全稀有( Rare )物品？(无效)
    -unique -unique 全独特(Unique)物品？(无效)
    7.界面选项
    -act -act 设置初始位置为第幕
    8.Debug选项
    -log -log 激活log(无效)
    -msglog -msglog 激活msglog
    -safe -safemode 安全模式？
    -seed -seed 设置地图种子(ala5:可理解为地图ID)为
    -cheats -cheats 不清初
    -ns -nosound 无声模式
    -questall -questall 不清楚
    9.文件输入输出选项
    -npl -nopreload 不预读取游戏文件
    -direct -direct 直接从硬盘上(ala5:而非mpq文件中)读取数据
    -lem -lowend 不清楚
    -nocompress -nocompress 无压缩
    -comint -comint 动态数据结构(别碰它)
    -token -token 设置关闭游戏的令牌为
    -gamepass -gamepass 设置游戏密码为
    -skiptobnet -skiptobnet 直接进入battle.net
    10.定制选项
    -client -client 客户端模式
    -server -server 服务器端模式，需要d2server.dll
    -launch -launch 运行模式(默认)
    -notitle -notitle 无窗口标题栏
    -res800 -res800 窗口大小为800x600(仅适用于D2，对D2X无效)
    -res640 -res640 窗口大小为640x480(仅适用于D2，对D2X无效)
    -nonotify -nonotify 关闭错误信息报警
    -noexit -noexit 不自动退出
    -autorest -autorest 退出后自动重新启动游戏
    -multiclient -multiclient 1个cdkey可以启动多个客户端游戏
    -nohook -nohook 禁止Windows钩子
    -nochar -nochar 禁止角色图像
    -clientexit -clientexit 退出游戏时自动关闭客户端游戏程序
    -noscript -noscript 不读取脚本
    -noplugin -noplugin 不导入Plug-in
    -locale -locale 设置语言为：ENG(英语)，CHI(中文)
    -hookwnd -hookwnd 设置钩子窗口类为
    -hookexe -hookexe 设置钩子版本校验game.exe为
    -servername -servername 设置游戏服务器端名字为
    -title -title 设置窗口标题为
    *********************************************************************
    一般情况下:
    -direct -txt
    -mpq file.mpq
    -w
    -locale chi
    这几个就够用了
    第一个是给另类模式调试用的，修改BIN文件比较麻烦，修改TXT文件，然后生成
    BIN文件，如果不需要生成BIN文件，参数还可以增加一个 -rtx
    第二个也可以用于快速加载CDK文件，把2个CDK的MPQ文件作为2个快捷方式，倒
    装备相当方便。
    第三个应该都知道，窗口模式，不多说。
    第四个是把其他语言转化为繁体中文..... 奥美版推荐使用.......需要鸟语版
    的参数改为 -locale eng 即可

    D2loader常用参数使用说明
    为"Diablo II.exe"创建一个快捷方式，就可以加上相应的参数实现不同的启动方式了，多个参数中用空格分开，如果字符串参数中有空格就用双引号引起来，如:
    -title "Diablo II"，就可以使Diablo窗口标题为"Diablo II"，如果不加双引号就成了"Diablo"


    D2loader会自动加载Diablo文件夹下Plugin文件夹里的文件，而用-pdir参数可是指定别的文件夹
    选出常用的参数说明
    红色为最常用参数
    代表数字, 代表字符串
    -w 以窗口模式运行Diablo
    -lq 低图像质量(挂bot常用)
    -ns 无声模式
    -locale 设置语言为 : ENG(英语)，CHI(中文)
    -title 设置窗口标题为

    -skiptobnet 直接进入Battle.net，只是省了点一下鼠标
    -res800 窗口大小为800x600(仅适用于D2，对D2X无效)
    -res640 窗口大小为640x480(仅适用于D2，对D2X无效)
    -notitle 无窗口标题栏
    -nonotify 关闭错误信息报警而直接退出
    -noexit 不自动退出
    -nochar 禁止角色图像
    -pdir 指定插件所在目录
    -mpq file.mpq 加载文件file.mpq，一般用在加载cdkey.mpq，或者加载Mod
    -txt 加载Mod时常用，一般游戏不用

    比较常用的
    -direct -w -lq -title "n" -pdir d2cn -locale eng E文
    -nonotify -title -lq -w -pdir d2cn 中文
    */
}

#define PATCH_FINISH    0xFFFFFFFF
#define PATCH_JMP               0x000000E9
#define PATCH_CALL              0x000000E8
#define PATCH_RETN              0x000000C3
#define PATCH_RETN4             0x000004C2
#define PATCH_RETN8             0x000008C2
#define PATCH_RETN0C            0x00000CC2
#define PATCH_RETN10            0x000010C2
#define PATCH_RETN14            0x000014C2
#define PATCH_RETN18            0x000018C2
#define PATCH_RETN1C            0x00001CC2
#define PATCH_NOPBLOCK          0x90909090

typedef struct 
{
    DWORD dwAddress;
    DWORD dwData;
    BOOL boolRelative;
    size_t iPatchSize;
} DLLPatchStrc;

static BOOL D2SE_ApplyPatch(void* hGame, const DWORD dwBaseAddress, const DLLPatchStrc* pstPatch)
{
    while ( PATCH_FINISH != pstPatch->dwAddress )
    {
        int iReturn = 0;
        DWORD dwAddress = pstPatch->dwAddress;
        if  ( !dwAddress )
        {
            return FALSE;
        }

        dwAddress += dwBaseAddress;

        DWORD dwData = pstPatch->dwData;
        if ( pstPatch->boolRelative )
        {
            dwData = dwData - (dwAddress + sizeof(dwData));
        }

        void* hAddress = (void*)dwAddress;
        DWORD dwOldPage;

        if ( pstPatch->iPatchSize > 0 )
        {
            BYTE abBuffer[1024];

            for ( size_t i = 0; i < pstPatch->iPatchSize; i++ )
            {
                abBuffer[i] = (BYTE)dwData;
            }

            VirtualProtect(hAddress, pstPatch->iPatchSize, PAGE_EXECUTE_READWRITE, &dwOldPage);
            iReturn = WriteProcessMemory(hGame, hAddress, &abBuffer, pstPatch->iPatchSize, 0);
            VirtualProtect(hAddress, pstPatch->iPatchSize, dwOldPage, 0);
        }
        else
        {
            VirtualProtect(hAddress, sizeof(dwData), PAGE_EXECUTE_READWRITE, &dwOldPage);
            iReturn = WriteProcessMemory(hGame, hAddress, &dwData, sizeof(dwData), 0);
            VirtualProtect(hAddress, sizeof(dwData), dwOldPage, 0);
        }

        if ( 0 == iReturn )
        {
            return FALSE;
        }

        pstPatch++;
    }

    return TRUE;
}

static DWORD WINAPI D2Loader_GetMpqPath(HMODULE hModule, LPTSTR lpFilename, DWORD nSize)
{
    char acTempPath[MAX_PATH] = {0};
    char acTempPath2[MAX_PATH] = {0};
    DWORD dwResult;
    char *pcTemp;
    DWORD i;

    dwResult = GetModuleFileName(hModule, acTempPath, sizeof(acTempPath));
    if ( 0 == dwResult || 0 == acTempPath[0] )
    {
        return 0;
    }

    pcTemp = strrchr(acTempPath, '\\');
    if ( !pcTemp )
    {
        strcpy_s(lpFilename, nSize, acTempPath);
        return dwResult;
    }

    memcpy(acTempPath2, acTempPath, (DWORD)(pcTemp - acTempPath));
    sprintf_s(&acTempPath2[strlen(acTempPath2)], sizeof(acTempPath2) - strlen(acTempPath2), "\\%s", lpFilename);
    if ( _access(acTempPath2, 0) == 0 )
    {
        strcpy_s(lpFilename, nSize, acTempPath);
        return dwResult;
    }

    for ( i = 0; i < m_dwGlobalMpqPath; ++i )
    {
        memset(acTempPath2, 0, sizeof(acTempPath2));
        strcpy_s(acTempPath2, sizeof(acTempPath2), m_aacGlobalMpqPath[i]);
        while ( '\\' == acTempPath2[strlen(acTempPath2) - 1] )
        {
            acTempPath2[strlen(acTempPath2) - 1] = 0;
        }
        sprintf_s(&acTempPath2[strlen(acTempPath2)], sizeof(acTempPath2) - strlen(acTempPath2), "\\%s", lpFilename);

        if ( _access(acTempPath2, 0) == 0 )
        {
            break;
        }
    }

    strcpy_s(lpFilename, nSize, acTempPath2);
    return strlen(lpFilename);
}

static DWORD WINAPI D2Loader_GetMpqPathExp(LPCTSTR lpFileName)
{
    DWORD i;
    char acTempPath[MAX_PATH] = {0};

    if ( !strstr(lpFileName, "d2exp.mpq") )
    {
        return GetFileAttributes(lpFileName);
    }

    for ( i = 0; i < m_dwGlobalMpqPath; ++i )
    {
        memset(acTempPath, 0, sizeof(acTempPath));
        strcpy_s(acTempPath, sizeof(acTempPath), m_aacGlobalMpqPath[i]);
        while ( '\\' == acTempPath[strlen(acTempPath) - 1] )
        {
            acTempPath[strlen(acTempPath) - 1] = 0;
        }
        sprintf_s(&acTempPath[strlen(acTempPath)], sizeof(acTempPath) - strlen(acTempPath), "\\%s", "d2exp.mpq");
        if ( _access(acTempPath, 0) == 0 )
        {
            return GetFileAttributes(acTempPath);
        }
    }

    return GetFileAttributes(lpFileName);
}

static DWORD WINAPI D2Loader_GetMpqPathChar(LPCTSTR lpFileName)
{
    DWORD i;
    char acTempPath[MAX_PATH] = {0};

    if ( !strstr(lpFileName, "d2char.mpq") )
    {
        return GetFileAttributes(lpFileName);
    }

    for ( i = 0; i < m_dwGlobalMpqPath; ++i )
    {
        memset(acTempPath, 0, sizeof(acTempPath));
        strcpy_s(acTempPath, sizeof(acTempPath), m_aacGlobalMpqPath[i]);
        while ( '\\' == acTempPath[strlen(acTempPath) - 1] )
        {
            acTempPath[strlen(acTempPath) - 1] = 0;
        }
        sprintf_s(&acTempPath[strlen(acTempPath)], sizeof(acTempPath) - strlen(acTempPath), "\\%s", "d2exp.mpq");
        if ( _access(acTempPath, 0) == 0 )
        {
            return GetFileAttributes(acTempPath);
        }
    }

    return GetFileAttributes(lpFileName);
}

static void D2Loader_PatchMpqPath()
{
    if ( 0 == m_aacGlobalMpqPath[0][0] )
    {
        return;
    }

    switch ( m_iGameVersion )
    {
        case V109d:
        {
            static const DLLPatchStrc astPatches[] =
            {//更新获取mpq的文件路径
                {0x143B7, PATCH_CALL, FALSE, 0x01},  //call
                {0x143B8, (DWORD)D2Loader_GetMpqPath, TRUE, 0x00},
                {0x143BC, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };
            static const DLLPatchStrc astPatches2[] =
            {//判断是否资料片，d2exp.mpq，否则会判断为非资料片版本
                {0xC0A0, (DWORD)0xBE, FALSE, 0x01},
                {0xC0A1, (DWORD)D2Loader_GetMpqPathExp, FALSE, 0x00},
                {0xC0A5, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };
            static const DLLPatchStrc astPatches3[] =
            {//判断是否资料片，d2char.mpq，否则会无法开启TCP/IP多人游戏，只能单机
                {0x7B49, (DWORD)0xBE, FALSE, 0x01},
                {0x7B4A, (DWORD)D2Loader_GetMpqPathChar, FALSE, 0x00},
                {0x7B4E, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            D2SE_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Win.dll"), astPatches);
            D2SE_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Fog.dll"), astPatches2);
            D2SE_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Win.dll"), astPatches3);
        }
            break;

        case V110f:
        {
            static const DLLPatchStrc astPatches[] =
            {//更新获取mpq的文件路径
                {0x12455, PATCH_CALL, FALSE, 0x01},  //call
                {0x12456, (DWORD)D2Loader_GetMpqPath, TRUE, 0x00},
                {0x1245A, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };
            static const DLLPatchStrc astPatches2[] =
            {//判断是否资料片，d2exp.mpq，否则会判断为非资料片版本
                {0xD770, (DWORD)0xBE, FALSE, 0x01},
                {0xD771, (DWORD)D2Loader_GetMpqPathExp, FALSE, 0x00},
                {0xD775, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };
            static const DLLPatchStrc astPatches3[] =
            {//判断是否资料片，d2char.mpq，否则会无法开启TCP/IP多人游戏，只能单机
                {0x5E19, (DWORD)0xBE, FALSE, 0x01},
                {0x5E1A, (DWORD)D2Loader_GetMpqPathChar, FALSE, 0x00},
                {0x5E1E, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            D2SE_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Win.dll"), astPatches);
            D2SE_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Fog.dll"), astPatches2);
            D2SE_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Win.dll"), astPatches3);
        }
            break;

        case V111b:
        {
            static const DLLPatchStrc astPatches[] =
            {//更新获取mpq的文件路径
                {0x7BC6, PATCH_CALL, FALSE, 0x01},  //call
                {0x7BC7, (DWORD)D2Loader_GetMpqPath, TRUE, 0x00},
                {0x7BCB, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };
            static const DLLPatchStrc astPatches2[] =
            {//判断是否资料片，d2exp.mpq，否则会判断为非资料片版本
                {0x1F700, (DWORD)0xBE, FALSE, 0x01},
                {0x1F701, (DWORD)D2Loader_GetMpqPathExp, FALSE, 0x00},
                {0x1F705, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };
            static const DLLPatchStrc astPatches3[] =
            {//判断是否资料片，d2char.mpq，否则会无法开启TCP/IP多人游戏，只能单机
                {0xC1F9, (DWORD)0xBE, FALSE, 0x01},
                {0xC1FA, (DWORD)D2Loader_GetMpqPathChar, FALSE, 0x00},
                {0xC1FE, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            D2SE_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Win.dll"), astPatches);
            D2SE_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Fog.dll"), astPatches2);
            D2SE_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Win.dll"), astPatches3);
        }
            break;

        case V112a:
        {
            static const DLLPatchStrc astPatches[] =
            {//更新获取mpq的文件路径
                {0x7DE6, PATCH_CALL, FALSE, 0x01},  //call
                {0x7DE7, (DWORD)D2Loader_GetMpqPath, TRUE, 0x00},
                {0x7DEB, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };
            static const DLLPatchStrc astPatches2[] =
            {//判断是否资料片，d2exp.mpq，否则会判断为非资料片版本
                {0x1F9E0, (DWORD)0xBE, FALSE, 0x01},
                {0x1F9E1, (DWORD)D2Loader_GetMpqPathExp, FALSE, 0x00},
                {0x1F9E5, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };
            static const DLLPatchStrc astPatches3[] =
            {//判断是否资料片，d2char.mpq，否则会无法开启TCP/IP多人游戏，只能单机
                {0x87F9, (DWORD)0xBE, FALSE, 0x01},
                {0x87FA, (DWORD)D2Loader_GetMpqPathChar, FALSE, 0x00},
                {0x87FE, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            D2SE_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Win.dll"), astPatches);
            D2SE_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Fog.dll"), astPatches2);
            D2SE_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Win.dll"), astPatches3);
        }
            break;

        case V113c:
        {
            static const DLLPatchStrc astPatches[] =
            {//更新获取mpq的文件路径
                {0x7E06, PATCH_CALL, FALSE, 0x01},  //call
                {0x7E07, (DWORD)D2Loader_GetMpqPath, TRUE, 0x00},
                {0x7E0B, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };
            static const DLLPatchStrc astPatches2[] =
            {//判断是否资料片，d2exp.mpq，否则会判断为非资料片版本
                {0x1F9F0, (DWORD)0xBE, FALSE, 0x01},
                {0x1F9F1, (DWORD)D2Loader_GetMpqPathExp, FALSE, 0x00},
                {0x1F9F5, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };
            static const DLLPatchStrc astPatches3[] =
            {//判断是否资料片，d2char.mpq，否则会无法开启TCP/IP多人游戏，只能单机
                {0xA499, (DWORD)0xBE, FALSE, 0x01},
                {0xA49A, (DWORD)D2Loader_GetMpqPathChar, FALSE, 0x00},
                {0xA49E, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            D2SE_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Win.dll"), astPatches);
            D2SE_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Fog.dll"), astPatches2);
            D2SE_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Win.dll"), astPatches3);
        }
            break;

        case V113d:
        {
            static const DLLPatchStrc astPatches[] =
            {//更新获取mpq的文件路径
                {0x7DF6, PATCH_CALL, FALSE, 0x01},  //call
                {0x7DF7, (DWORD)D2Loader_GetMpqPath, TRUE, 0x00},
                {0x7DFB, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };
            static const DLLPatchStrc astPatches2[] =
            {//判断是否资料片，d2exp.mpq，否则会判断为非资料片版本
                {0x1FB20, (DWORD)0xBE, FALSE, 0x01},
                {0x1FB21, (DWORD)D2Loader_GetMpqPathExp, FALSE, 0x00},
                {0x1FB25, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };
            static const DLLPatchStrc astPatches3[] =
            {//判断是否资料片，d2char.mpq，否则会无法开启TCP/IP多人游戏，只能单机
                {0xCCB9, (DWORD)0xBE, FALSE, 0x01},
                {0xCCBA, (DWORD)D2Loader_GetMpqPathChar, FALSE, 0x00},
                {0xCCBE, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            D2SE_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Win.dll"), astPatches);
            D2SE_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Fog.dll"), astPatches2);
            D2SE_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Win.dll"), astPatches3);
        }
            break;

        default:
            break;
    }
}

static void D2Loader_PatchDepFix()
{
    if ( TRUE != m_boolDepFix )
    {
        return;
    }

    switch ( m_iGameVersion )
    {
        case V109d:
        {
            static const DLLPatchStrc astPatches[] =
            {//跳过IX86ver1.dll的版本检查
                {0x44D0, 0xB8, FALSE, 0x01},  //mov eax,0x1;retn 0xC
                {0x44D1, 0x01, FALSE, 0x01},
                {0x44D2, 0x00, FALSE, 0x01},
                {0x44D3, 0x00, FALSE, 0x01},
                {0x44D4, 0x00, FALSE, 0x01},
                {0x44D5, 0xC2, FALSE, 0x01},
                {0x44D6, 0x0C, FALSE, 0x01},
                {0x44D7, 0x00, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            D2SE_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Bnclient.dll"), astPatches);
        }
            break;

        case V110f:
        {
            static const DLLPatchStrc astPatches[] =
            {//跳过IX86ver1.dll的版本检查
                {0x4480, 0xB8, FALSE, 0x01},  //mov eax,0x1;retn 0xC
                {0x4481, 0x01, FALSE, 0x01},
                {0x4482, 0x00, FALSE, 0x01},
                {0x4483, 0x00, FALSE, 0x01},
                {0x4484, 0x00, FALSE, 0x01},
                {0x4485, 0xC2, FALSE, 0x01},
                {0x4486, 0x0C, FALSE, 0x01},
                {0x4487, 0x00, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            D2SE_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Bnclient.dll"), astPatches);
        }
            break;

        case V111b:
        {
            static const DLLPatchStrc astPatches[] =
            {//跳过IX86ver1.dll的版本检查
                {0xCBA0, 0xB8, FALSE, 0x01},  //mov eax,0x1;retn 0xC
                {0xCBA1, 0x01, FALSE, 0x01},
                {0xCBA2, 0x00, FALSE, 0x01},
                {0xCBA3, 0x00, FALSE, 0x01},
                {0xCBA4, 0x00, FALSE, 0x01},
                {0xCBA5, 0xC2, FALSE, 0x01},
                {0xCBA6, 0x0C, FALSE, 0x01},
                {0xCBA7, 0x00, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            D2SE_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Bnclient.dll"), astPatches);
        }
            break;

        case V112a:
        {
            static const DLLPatchStrc astPatches[] =
            {//跳过IX86ver1.dll的版本检查
                {0xC200, 0xB8, FALSE, 0x01},  //mov eax,0x1;retn 0xC
                {0xC201, 0x01, FALSE, 0x01},
                {0xC202, 0x00, FALSE, 0x01},
                {0xC203, 0x00, FALSE, 0x01},
                {0xC204, 0x00, FALSE, 0x01},
                {0xC205, 0xC2, FALSE, 0x01},
                {0xC206, 0x0C, FALSE, 0x01},
                {0xC207, 0x00, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            D2SE_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Bnclient.dll"), astPatches);
        }
            break;

        case V113c:
        {
            static const DLLPatchStrc astPatches[] =
            {//跳过IX86ver1.dll的版本检查
                {0xD320, 0xB8, FALSE, 0x01},  //mov eax,0x1;retn 0xC
                {0xD321, 0x01, FALSE, 0x01},
                {0xD322, 0x00, FALSE, 0x01},
                {0xD323, 0x00, FALSE, 0x01},
                {0xD324, 0x00, FALSE, 0x01},
                {0xD325, 0xC2, FALSE, 0x01},
                {0xD326, 0x0C, FALSE, 0x01},
                {0xD327, 0x00, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            D2SE_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Bnclient.dll"), astPatches);
        }
            break;

        case V113d:
        {
            static const DLLPatchStrc astPatches[] =
            {//跳过IX86ver1.dll的版本检查
                {0x11090, 0xB8, FALSE, 0x01},  //mov eax,0x1;retn 0xC
                {0x11091, 0x01, FALSE, 0x01},
                {0x11092, 0x00, FALSE, 0x01},
                {0x11093, 0x00, FALSE, 0x01},
                {0x11094, 0x00, FALSE, 0x01},
                {0x11095, 0xC2, FALSE, 0x01},
                {0x11096, 0x0C, FALSE, 0x01},
                {0x11097, 0x00, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            D2SE_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Bnclient.dll"), astPatches);
        }
            break;

        default:
            break;
    }
}

static void D2Loader_PatchMultiOpen()
{
    if ( TRUE != m_boolMultiOpen )
    {
        return;
    }

    switch ( m_iGameVersion )
    {
        case V109d:
        {
            static const DLLPatchStrc astPatches[] =
            {
                {0x447C, 0xEB, FALSE, 0x01},  //je short 6FA8B6F7-->jmp

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            D2SE_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2gfx.dll"), astPatches);
        }
            break;

        case V110f:
        {
            static const DLLPatchStrc astPatches[] =
            {
                {0x446A, 0xEB, FALSE, 0x01},  //je short 6FA8B6F7-->jmp

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            D2SE_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2gfx.dll"), astPatches);
        }
            break;

        case V111b:
        {
            static const DLLPatchStrc astPatches[] =
            {
                {0x84AF, 0xEB, FALSE, 0x01},  //je short 6FA88606-->jmp

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            D2SE_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2gfx.dll"), astPatches);
        }
            break;

        case V112a:
        {
            static const DLLPatchStrc astPatches[] =
            {
                {0x894F, 0xEB, FALSE, 0x01},  //je short 6FA88996-->jmp

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            D2SE_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2gfx.dll"), astPatches);
        }
            break;

        case V113c:
        {
            static const DLLPatchStrc astPatches[] =
            {
                {0x85BF, 0xEB, FALSE, 0x01},  //je short 6FA88606-->jmp

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            D2SE_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2gfx.dll"), astPatches);
        }
            break;

        case V113d:
        {
            static const DLLPatchStrc astPatches[] =
            {
                {0xB6B0, 0xEB, FALSE, 0x01},  //je short 6FA8B6F7-->jmp

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            D2SE_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2gfx.dll"), astPatches);
        }
            break;

        default:
            break;
    }
}

static void *m_pvClientData = NULL;

static void *D2Loader_InitClientData(ST_CLIENT_DATA *pstClientData)
{
    if ( NULL != m_pvClientData )
    {
        return m_pvClientData;
    }

    switch ( m_iGameVersion )
    {
        case V109d:
            m_pvClientData = malloc(sizeof(ST_CLIENT_DATA_109));
            break;

        case V110f:
            m_pvClientData = malloc(sizeof(ST_CLIENT_DATA_110));
            break;

        case V111b:
            m_pvClientData = malloc(sizeof(ST_CLIENT_DATA_111));
            break;

        case V112a:
            m_pvClientData = malloc(sizeof(ST_CLIENT_DATA_112));
            break;

        case V113c:
        case V113d:
            m_pvClientData = pstClientData;
            break;

        default:
            break;
    }

    return m_pvClientData;
}

static void D2Loader_FreeClientData(ST_CLIENT_DATA *pstClientData)
{
    if ( NULL != m_pvClientData && m_pvClientData != pstClientData )
    {
        free(m_pvClientData);
    }
    m_pvClientData = NULL;
}

static void D2Loader_PutClientData(ST_CLIENT_DATA *pstClientData)
{
    switch ( m_iGameVersion )
    {
        case V109d:
            memcpy(m_pvClientData, pstClientData, 5);
            memcpy((void *)((DWORD)m_pvClientData + 5), (void *)((DWORD)pstClientData + 6), (DWORD)&pstClientData->teen - (DWORD)&pstClientData->glide_mode);
            memcpy(&(((ST_CLIENT_DATA_109 *)m_pvClientData)->no_sound), &pstClientData->no_sound, 3);
            memcpy(&(((ST_CLIENT_DATA_109 *)m_pvClientData)->bnet_callbacks), &pstClientData->bnet_callbacks, sizeof(ST_CLIENT_DATA) - ((DWORD)&pstClientData->bnet_callbacks - (DWORD)pstClientData));
            break;

        case V110f:
            memcpy(m_pvClientData, pstClientData, 5);
            memcpy((void *)((DWORD)m_pvClientData + 5), (void *)((DWORD)pstClientData + 6), (DWORD)&pstClientData->sound_background - (DWORD)&pstClientData->glide_mode);
            memcpy(&(((ST_CLIENT_DATA_110 *)m_pvClientData)->bnet_callbacks), &pstClientData->bnet_callbacks, sizeof(ST_CLIENT_DATA) - ((DWORD)&pstClientData->bnet_callbacks - (DWORD)pstClientData));
            break;

        case V111b:
            memcpy(m_pvClientData, pstClientData, 5);
            memcpy((void *)((DWORD)m_pvClientData + 5), (void *)((DWORD)pstClientData + 6), (DWORD)&pstClientData->sound_background - (DWORD)&pstClientData->glide_mode);
            memcpy(&(((ST_CLIENT_DATA_111 *)m_pvClientData)->bnet_callbacks), &pstClientData->bnet_callbacks, sizeof(ST_CLIENT_DATA) - ((DWORD)&pstClientData->bnet_callbacks - (DWORD)pstClientData));
            break;

        case V112a:
            memcpy(m_pvClientData, pstClientData, 5);
            memcpy((void *)((DWORD)m_pvClientData + 5), (void *)((DWORD)pstClientData + 6), (DWORD)&pstClientData->sound_background - (DWORD)&pstClientData->glide_mode);
            memcpy(&(((ST_CLIENT_DATA_112 *)m_pvClientData)->bnet_callbacks), &pstClientData->bnet_callbacks, sizeof(ST_CLIENT_DATA) - ((DWORD)&pstClientData->bnet_callbacks - (DWORD)pstClientData));
            break;

        case V113c:
        case V113d:
            break;

        default:
            break;
    }
}

static void D2Loader_GetClientData(ST_CLIENT_DATA *pstClientData)
{
    switch ( m_iGameVersion )
    {
        case V109d:
            memcpy(pstClientData, m_pvClientData, 5);
            memcpy((void *)((DWORD)pstClientData + 6), (void *)((DWORD)m_pvClientData + 5), (DWORD)&pstClientData->teen - (DWORD)&pstClientData->glide_mode);
            memcpy(&pstClientData->no_sound, &(((ST_CLIENT_DATA_109 *)m_pvClientData)->no_sound), 3);
            memcpy(&pstClientData->bnet_callbacks, &(((ST_CLIENT_DATA_109 *)m_pvClientData)->bnet_callbacks), sizeof(ST_CLIENT_DATA) - ((DWORD)&pstClientData->bnet_callbacks - (DWORD)pstClientData));
            break;

        case V110f:
            memcpy(pstClientData, m_pvClientData, 5);
            memcpy((void *)((DWORD)pstClientData + 6), (void *)((DWORD)m_pvClientData + 5), (DWORD)&pstClientData->sound_background - (DWORD)&pstClientData->glide_mode);
            memcpy(&pstClientData->bnet_callbacks, &(((ST_CLIENT_DATA_110 *)m_pvClientData)->bnet_callbacks), sizeof(ST_CLIENT_DATA) - ((DWORD)&pstClientData->bnet_callbacks - (DWORD)pstClientData));
            break;

        case V111b:
            memcpy(pstClientData, m_pvClientData, 5);
            memcpy((void *)((DWORD)pstClientData + 6), (void *)((DWORD)m_pvClientData + 5), (DWORD)&pstClientData->sound_background - (DWORD)&pstClientData->glide_mode);
            memcpy(&pstClientData->bnet_callbacks, &(((ST_CLIENT_DATA_111 *)m_pvClientData)->bnet_callbacks), sizeof(ST_CLIENT_DATA) - ((DWORD)&pstClientData->bnet_callbacks - (DWORD)pstClientData));
            break;

        case V112a:
            memcpy(pstClientData, m_pvClientData, 5);
            memcpy((void *)((DWORD)pstClientData + 6), (void *)((DWORD)m_pvClientData + 5), (DWORD)&pstClientData->sound_background - (DWORD)&pstClientData->glide_mode);
            memcpy(&pstClientData->bnet_callbacks, &(((ST_CLIENT_DATA_112 *)m_pvClientData)->bnet_callbacks), sizeof(ST_CLIENT_DATA) - ((DWORD)&pstClientData->bnet_callbacks - (DWORD)pstClientData));
            break;

        case V113c:
        case V113d:
            break;

        default:
            break;
    }
}

static void D2Loader_CheckStruct()
{
#define CHECK_STRUCT(strname, varname, offset)  if ( offset != (DWORD)&(strname->varname) - (DWORD)strname ) {printf("fail "#varname"[%d] for "#strname"[%d]\r\n", (DWORD)&(strname->varname) - (DWORD)strname, offset);return;}
    ST_CLIENT_DATA *pstClientData = (ST_CLIENT_DATA *)malloc(sizeof(ST_CLIENT_DATA));
    ST_CLIENT_DATA_109 *pstClientData109 = (ST_CLIENT_DATA_109 *)malloc(sizeof(ST_CLIENT_DATA_109));
    ST_CLIENT_DATA_110 *pstClientData110 = (ST_CLIENT_DATA_110 *)malloc(sizeof(ST_CLIENT_DATA_110));
    ST_CLIENT_DATA_111 *pstClientData111 = (ST_CLIENT_DATA_111 *)malloc(sizeof(ST_CLIENT_DATA_111));
    ST_CLIENT_DATA_112 *pstClientData112 = (ST_CLIENT_DATA_112 *)malloc(sizeof(ST_CLIENT_DATA_112));

    //1.09d
    printf("start checking 1.09d\r\n");
    CHECK_STRUCT(pstClientData109, window_mode, 0x04)
    CHECK_STRUCT(pstClientData109, glide_mode, 0x05)
    CHECK_STRUCT(pstClientData109, opengl_mode, 0x06)
    CHECK_STRUCT(pstClientData109, rave_mode, 0x07)
    CHECK_STRUCT(pstClientData109, d3d_mode, 0x08)
    CHECK_STRUCT(pstClientData109, perspective, 0x09)
    CHECK_STRUCT(pstClientData109, low_quality, 0x0A)
    CHECK_STRUCT(pstClientData109, gamma, 0x0B)
    CHECK_STRUCT(pstClientData109, vsync, 0x0F)
    CHECK_STRUCT(pstClientData109, frame_rate, 0x10)
    CHECK_STRUCT(pstClientData109, join_id, 0x18)
    CHECK_STRUCT(pstClientData109, game_name, 0x1A)
    CHECK_STRUCT(pstClientData109, game_ip, 0x32)
    CHECK_STRUCT(pstClientData109, bnet_ip, 0x4A)
    CHECK_STRUCT(pstClientData109, mcp_ip, 0x62)
    CHECK_STRUCT(pstClientData109, no_pk, 0x7E)
    CHECK_STRUCT(pstClientData109, open_c, 0x7F)
    CHECK_STRUCT(pstClientData109, amazon, 0x80)
    CHECK_STRUCT(pstClientData109, paladin, 0x81)
    CHECK_STRUCT(pstClientData109, sorceress, 0x82)
    CHECK_STRUCT(pstClientData109, necromancer, 0x83)
    CHECK_STRUCT(pstClientData109, barbarian, 0x84)
    CHECK_STRUCT(pstClientData109, invincible, 0x87)
    CHECK_STRUCT(pstClientData109, player_name, 0xB8)
    CHECK_STRUCT(pstClientData109, realm_name, 0xD0)
    CHECK_STRUCT(pstClientData109, c_temp, 0x01E8)
    CHECK_STRUCT(pstClientData109, no_monsters, 0x01EC)
    CHECK_STRUCT(pstClientData109, monster_class, 0x01ED)
    CHECK_STRUCT(pstClientData109, monster_info, 0x01F1)
    CHECK_STRUCT(pstClientData109, monster_debug, 0x01F2)
    CHECK_STRUCT(pstClientData109, item_rare, 0x01F6)
    CHECK_STRUCT(pstClientData109, item_unique, 0x01F7)
    CHECK_STRUCT(pstClientData109, act, 0x01FA)
    CHECK_STRUCT(pstClientData109, no_preload, 0x01FE)
    CHECK_STRUCT(pstClientData109, direct, 0x01FF)
    CHECK_STRUCT(pstClientData109, low_end, 0x0200)
    CHECK_STRUCT(pstClientData109, no_gfx_compress, 0x0201)
    CHECK_STRUCT(pstClientData109, arena, 0x0202)
    CHECK_STRUCT(pstClientData109, txt, 0x0210)
    CHECK_STRUCT(pstClientData109, log, 0x0211)
    CHECK_STRUCT(pstClientData109, msg_log, 0x0212)
    CHECK_STRUCT(pstClientData109, safe_mode, 0x0213)
    CHECK_STRUCT(pstClientData109, no_save, 0x0214)
    CHECK_STRUCT(pstClientData109, seed, 0x0215)
    CHECK_STRUCT(pstClientData109, cheats, 0x0219)
    CHECK_STRUCT(pstClientData109, no_sound, 0x021A)
    CHECK_STRUCT(pstClientData109, quests, 0x021B)
    CHECK_STRUCT(pstClientData109, skip_to_bnet, 0x0355)
    printf("finish checking 1.09d\r\n");

    //1.10f
    printf("start checking 1.10f\r\n");
    CHECK_STRUCT(pstClientData110, window_mode, 0x04)
    CHECK_STRUCT(pstClientData110, glide_mode, 0x05)
    CHECK_STRUCT(pstClientData110, opengl_mode, 0x06)
    CHECK_STRUCT(pstClientData110, rave_mode, 0x07)
    CHECK_STRUCT(pstClientData110, d3d_mode, 0x08)
    CHECK_STRUCT(pstClientData110, perspective, 0x09)
    CHECK_STRUCT(pstClientData110, low_quality, 0x0A)
    CHECK_STRUCT(pstClientData110, gamma, 0x0B)
    CHECK_STRUCT(pstClientData110, vsync, 0x0F)
    CHECK_STRUCT(pstClientData110, frame_rate, 0x10)
    CHECK_STRUCT(pstClientData110, join_id, 0x18)
    CHECK_STRUCT(pstClientData110, game_name, 0x1A)
    CHECK_STRUCT(pstClientData110, game_ip, 0x32)
    CHECK_STRUCT(pstClientData110, bnet_ip, 0x4A)
    CHECK_STRUCT(pstClientData110, mcp_ip, 0x62)
    CHECK_STRUCT(pstClientData110, no_pk, 0x7E)
    CHECK_STRUCT(pstClientData110, open_c, 0x7F)
    CHECK_STRUCT(pstClientData110, amazon, 0x80)
    CHECK_STRUCT(pstClientData110, paladin, 0x81)
    CHECK_STRUCT(pstClientData110, sorceress, 0x82)
    CHECK_STRUCT(pstClientData110, necromancer, 0x83)
    CHECK_STRUCT(pstClientData110, barbarian, 0x84)
    CHECK_STRUCT(pstClientData110, invincible, 0x87)
    CHECK_STRUCT(pstClientData110, player_name, 0xB8)
    CHECK_STRUCT(pstClientData110, realm_name, 0xD0)
    CHECK_STRUCT(pstClientData110, c_temp, 0x01E8)
    CHECK_STRUCT(pstClientData110, no_monsters, 0x01EC)
    CHECK_STRUCT(pstClientData110, monster_class, 0x01ED)
    CHECK_STRUCT(pstClientData110, monster_info, 0x01F1)
    CHECK_STRUCT(pstClientData110, monster_debug, 0x01F2)
    CHECK_STRUCT(pstClientData110, item_rare, 0x01F6)
    CHECK_STRUCT(pstClientData110, item_unique, 0x01F7)
    CHECK_STRUCT(pstClientData110, act, 0x01FA)
    CHECK_STRUCT(pstClientData110, no_preload, 0x01FE)
    CHECK_STRUCT(pstClientData110, direct, 0x01FF)
    CHECK_STRUCT(pstClientData110, low_end, 0x0200)
    CHECK_STRUCT(pstClientData110, no_gfx_compress, 0x0201)
    CHECK_STRUCT(pstClientData110, arena, 0x0202)
    CHECK_STRUCT(pstClientData110, txt, 0x0210)
    CHECK_STRUCT(pstClientData110, log, 0x0211)
    CHECK_STRUCT(pstClientData110, msg_log, 0x0212)
    CHECK_STRUCT(pstClientData110, safe_mode, 0x0213)
    CHECK_STRUCT(pstClientData110, no_save, 0x0214)
    CHECK_STRUCT(pstClientData110, seed, 0x0215)
    CHECK_STRUCT(pstClientData110, cheats, 0x0219)
    CHECK_STRUCT(pstClientData110, teen, 0x021A)
    CHECK_STRUCT(pstClientData110, no_sound, 0x021B)
    CHECK_STRUCT(pstClientData110, quests, 0x021C)
    CHECK_STRUCT(pstClientData110, build, 0x021E)
    CHECK_STRUCT(pstClientData110, skip_to_bnet, 0x0357)
    printf("finish checking 1.10f\r\n");

    //1.11b
    printf("start checking 1.11b\r\n");
    CHECK_STRUCT(pstClientData111, window_mode, 0x04)
    CHECK_STRUCT(pstClientData111, glide_mode, 0x05)
    CHECK_STRUCT(pstClientData111, opengl_mode, 0x06)
    CHECK_STRUCT(pstClientData111, rave_mode, 0x07)
    CHECK_STRUCT(pstClientData111, d3d_mode, 0x08)
    CHECK_STRUCT(pstClientData111, perspective, 0x09)
    CHECK_STRUCT(pstClientData111, low_quality, 0x0A)
    CHECK_STRUCT(pstClientData111, gamma, 0x0B)
    CHECK_STRUCT(pstClientData111, vsync, 0x0F)
    CHECK_STRUCT(pstClientData111, frame_rate, 0x10)
    CHECK_STRUCT(pstClientData111, join_id, 0x18)
    CHECK_STRUCT(pstClientData111, game_name, 0x1A)
    CHECK_STRUCT(pstClientData111, game_ip, 0x32)
    CHECK_STRUCT(pstClientData111, bnet_ip, 0x4A)
    CHECK_STRUCT(pstClientData111, mcp_ip, 0x62)
    CHECK_STRUCT(pstClientData111, no_pk, 0x7E)
    CHECK_STRUCT(pstClientData111, open_c, 0x7F)
    CHECK_STRUCT(pstClientData111, amazon, 0x80)
    CHECK_STRUCT(pstClientData111, paladin, 0x81)
    CHECK_STRUCT(pstClientData111, sorceress, 0x82)
    CHECK_STRUCT(pstClientData111, necromancer, 0x83)
    CHECK_STRUCT(pstClientData111, barbarian, 0x84)
    CHECK_STRUCT(pstClientData111, invincible, 0x87)
    CHECK_STRUCT(pstClientData111, player_name, 0xB8)
    CHECK_STRUCT(pstClientData111, realm_name, 0xD0)
    CHECK_STRUCT(pstClientData111, c_temp, 0x01E8)
    CHECK_STRUCT(pstClientData111, no_monsters, 0x01EC)
    CHECK_STRUCT(pstClientData111, monster_class, 0x01ED)
    CHECK_STRUCT(pstClientData111, monster_info, 0x01F1)
    CHECK_STRUCT(pstClientData111, monster_debug, 0x01F2)
    CHECK_STRUCT(pstClientData111, item_rare, 0x01F6)
    CHECK_STRUCT(pstClientData111, item_unique, 0x01F7)
    CHECK_STRUCT(pstClientData111, act, 0x01FA)
    CHECK_STRUCT(pstClientData111, no_preload, 0x01FE)
    CHECK_STRUCT(pstClientData111, direct, 0x01FF)
    CHECK_STRUCT(pstClientData111, low_end, 0x0200)
    CHECK_STRUCT(pstClientData111, no_gfx_compress, 0x0201)
    CHECK_STRUCT(pstClientData111, arena, 0x0202)
    CHECK_STRUCT(pstClientData111, txt, 0x0210)
    CHECK_STRUCT(pstClientData111, log, 0x0211)
    CHECK_STRUCT(pstClientData111, msg_log, 0x0212)
    CHECK_STRUCT(pstClientData111, safe_mode, 0x0213)
    CHECK_STRUCT(pstClientData111, no_save, 0x0214)
    CHECK_STRUCT(pstClientData111, seed, 0x0215)
    CHECK_STRUCT(pstClientData111, cheats, 0x0219)
    CHECK_STRUCT(pstClientData111, teen, 0x021A)
    CHECK_STRUCT(pstClientData111, no_sound, 0x021B)
    CHECK_STRUCT(pstClientData111, quests, 0x021C)
    CHECK_STRUCT(pstClientData111, build, 0x021E)
    CHECK_STRUCT(pstClientData111, skip_to_bnet, 0x0357)
    printf("finish checking 1.11b\r\n");

    //1.12a
    printf("start checking 1.12a\r\n");
    CHECK_STRUCT(pstClientData112, window_mode, 0x04)
    CHECK_STRUCT(pstClientData112, glide_mode, 0x05)
    CHECK_STRUCT(pstClientData112, opengl_mode, 0x06)
    CHECK_STRUCT(pstClientData112, rave_mode, 0x07)
    CHECK_STRUCT(pstClientData112, d3d_mode, 0x08)
    CHECK_STRUCT(pstClientData112, perspective, 0x09)
    CHECK_STRUCT(pstClientData112, low_quality, 0x0A)
    CHECK_STRUCT(pstClientData112, gamma, 0x0B)
    CHECK_STRUCT(pstClientData112, vsync, 0x0F)
    CHECK_STRUCT(pstClientData112, frame_rate, 0x10)
    CHECK_STRUCT(pstClientData112, join_id, 0x18)
    CHECK_STRUCT(pstClientData112, game_name, 0x1A)
    CHECK_STRUCT(pstClientData112, game_ip, 0x32)
    CHECK_STRUCT(pstClientData112, bnet_ip, 0x4A)
    CHECK_STRUCT(pstClientData112, mcp_ip, 0x62)
    CHECK_STRUCT(pstClientData112, no_pk, 0x7E)
    CHECK_STRUCT(pstClientData112, open_c, 0x7F)
    CHECK_STRUCT(pstClientData112, amazon, 0x80)
    CHECK_STRUCT(pstClientData112, paladin, 0x81)
    CHECK_STRUCT(pstClientData112, sorceress, 0x82)
    CHECK_STRUCT(pstClientData112, necromancer, 0x83)
    CHECK_STRUCT(pstClientData112, barbarian, 0x84)
    CHECK_STRUCT(pstClientData112, invincible, 0x87)
    CHECK_STRUCT(pstClientData112, player_name, 0xB8)
    CHECK_STRUCT(pstClientData112, realm_name, 0xD0)
    CHECK_STRUCT(pstClientData112, c_temp, 0x01E8)
    CHECK_STRUCT(pstClientData112, no_monsters, 0x01EC)
    CHECK_STRUCT(pstClientData112, monster_class, 0x01ED)
    CHECK_STRUCT(pstClientData112, monster_info, 0x01F1)
    CHECK_STRUCT(pstClientData112, monster_debug, 0x01F2)
    CHECK_STRUCT(pstClientData112, item_rare, 0x01F6)
    CHECK_STRUCT(pstClientData112, item_unique, 0x01F7)
    CHECK_STRUCT(pstClientData112, act, 0x01FA)
    CHECK_STRUCT(pstClientData112, no_preload, 0x01FE)
    CHECK_STRUCT(pstClientData112, direct, 0x01FF)
    CHECK_STRUCT(pstClientData112, low_end, 0x0200)
    CHECK_STRUCT(pstClientData112, no_gfx_compress, 0x0201)
    CHECK_STRUCT(pstClientData112, arena, 0x0202)
    CHECK_STRUCT(pstClientData112, txt, 0x0210)
    CHECK_STRUCT(pstClientData112, log, 0x0211)
    CHECK_STRUCT(pstClientData112, msg_log, 0x0212)
    CHECK_STRUCT(pstClientData112, safe_mode, 0x0213)
    CHECK_STRUCT(pstClientData112, no_save, 0x0214)
    CHECK_STRUCT(pstClientData112, seed, 0x0215)
    CHECK_STRUCT(pstClientData112, cheats, 0x0219)
    CHECK_STRUCT(pstClientData112, teen, 0x021A)
    CHECK_STRUCT(pstClientData112, no_sound, 0x021B)
    CHECK_STRUCT(pstClientData112, quests, 0x021C)
    CHECK_STRUCT(pstClientData112, build, 0x021E)
    CHECK_STRUCT(pstClientData112, skip_to_bnet, 0x0357)
    printf("finish checking 1.12a\r\n");

    //1.13c
    printf("start checking 1.13c\r\n");
    CHECK_STRUCT(pstClientData, window_mode, 0x04)
    CHECK_STRUCT(pstClientData, fix_aspect_ratio, 0x05)
    CHECK_STRUCT(pstClientData, glide_mode, 0x06)
    CHECK_STRUCT(pstClientData, opengl_mode, 0x07)
    CHECK_STRUCT(pstClientData, rave_mode, 0x08)
    CHECK_STRUCT(pstClientData, d3d_mode, 0x09)
    CHECK_STRUCT(pstClientData, perspective, 0x0A)
    CHECK_STRUCT(pstClientData, low_quality, 0x0B)
    CHECK_STRUCT(pstClientData, gamma, 0x0C)
    CHECK_STRUCT(pstClientData, vsync, 0x10)
    CHECK_STRUCT(pstClientData, frame_rate, 0x11)
    CHECK_STRUCT(pstClientData, join_id, 0x19)
    CHECK_STRUCT(pstClientData, game_name, 0x1B)
    CHECK_STRUCT(pstClientData, game_ip, 0x33)
    CHECK_STRUCT(pstClientData, bnet_ip, 0x4B)
    CHECK_STRUCT(pstClientData, mcp_ip, 0x63)
    CHECK_STRUCT(pstClientData, no_pk, 0x7F)
    CHECK_STRUCT(pstClientData, open_c, 0x80)
    CHECK_STRUCT(pstClientData, amazon, 0x81)
    CHECK_STRUCT(pstClientData, paladin, 0x82)
    CHECK_STRUCT(pstClientData, sorceress, 0x83)
    CHECK_STRUCT(pstClientData, necromancer, 0x84)
    CHECK_STRUCT(pstClientData, barbarian, 0x85)
    CHECK_STRUCT(pstClientData, invincible, 0x88)
    CHECK_STRUCT(pstClientData, player_name, 0xB9)
    CHECK_STRUCT(pstClientData, realm_name, 0xD1)
    CHECK_STRUCT(pstClientData, c_temp, 0x01E9)
    CHECK_STRUCT(pstClientData, no_monsters, 0x01ED)
    CHECK_STRUCT(pstClientData, monster_class, 0x01EE)
    CHECK_STRUCT(pstClientData, monster_info, 0x01F2)
    CHECK_STRUCT(pstClientData, monster_debug, 0x01F3)
    CHECK_STRUCT(pstClientData, item_rare, 0x01F7)
    CHECK_STRUCT(pstClientData, item_unique, 0x01F8)
    CHECK_STRUCT(pstClientData, act, 0x01FB)
    CHECK_STRUCT(pstClientData, no_preload, 0x01FF)
    CHECK_STRUCT(pstClientData, direct, 0x0200)
    CHECK_STRUCT(pstClientData, low_end, 0x0201)
    CHECK_STRUCT(pstClientData, no_gfx_compress, 0x0202)
    CHECK_STRUCT(pstClientData, arena, 0x0203)
    CHECK_STRUCT(pstClientData, txt, 0x0211)
    CHECK_STRUCT(pstClientData, log, 0x0212)
    CHECK_STRUCT(pstClientData, msg_log, 0x0213)
    CHECK_STRUCT(pstClientData, safe_mode, 0x0214)
    CHECK_STRUCT(pstClientData, no_save, 0x0215)
    CHECK_STRUCT(pstClientData, seed, 0x0216)
    CHECK_STRUCT(pstClientData, cheats, 0x021A)
    CHECK_STRUCT(pstClientData, teen, 0x021B)
    CHECK_STRUCT(pstClientData, no_sound, 0x021C)
    CHECK_STRUCT(pstClientData, quests, 0x021D)
    CHECK_STRUCT(pstClientData, build, 0x021F)
    CHECK_STRUCT(pstClientData, sound_background, 0x0220)
    CHECK_STRUCT(pstClientData, skip_to_bnet, 0x0359)
    printf("finish checking 1.13c\r\n");

    //1.13d
    printf("start checking 1.13d\r\n");
    CHECK_STRUCT(pstClientData, window_mode, 0x04)
    CHECK_STRUCT(pstClientData, fix_aspect_ratio, 0x05)
    CHECK_STRUCT(pstClientData, glide_mode, 0x06)
    CHECK_STRUCT(pstClientData, opengl_mode, 0x07)
    CHECK_STRUCT(pstClientData, rave_mode, 0x08)
    CHECK_STRUCT(pstClientData, d3d_mode, 0x09)
    CHECK_STRUCT(pstClientData, perspective, 0x0A)
    CHECK_STRUCT(pstClientData, low_quality, 0x0B)
    CHECK_STRUCT(pstClientData, gamma, 0x0C)
    CHECK_STRUCT(pstClientData, vsync, 0x10)
    CHECK_STRUCT(pstClientData, frame_rate, 0x11)
    CHECK_STRUCT(pstClientData, join_id, 0x19)
    CHECK_STRUCT(pstClientData, game_name, 0x1B)
    CHECK_STRUCT(pstClientData, game_ip, 0x33)
    CHECK_STRUCT(pstClientData, bnet_ip, 0x4B)
    CHECK_STRUCT(pstClientData, mcp_ip, 0x63)
    CHECK_STRUCT(pstClientData, no_pk, 0x7F)
    CHECK_STRUCT(pstClientData, open_c, 0x80)
    CHECK_STRUCT(pstClientData, amazon, 0x81)
    CHECK_STRUCT(pstClientData, paladin, 0x82)
    CHECK_STRUCT(pstClientData, sorceress, 0x83)
    CHECK_STRUCT(pstClientData, necromancer, 0x84)
    CHECK_STRUCT(pstClientData, barbarian, 0x85)
    CHECK_STRUCT(pstClientData, invincible, 0x88)
    CHECK_STRUCT(pstClientData, player_name, 0xB9)
    CHECK_STRUCT(pstClientData, realm_name, 0xD1)
    CHECK_STRUCT(pstClientData, c_temp, 0x01E9)
    CHECK_STRUCT(pstClientData, no_monsters, 0x01ED)
    CHECK_STRUCT(pstClientData, monster_class, 0x01EE)
    CHECK_STRUCT(pstClientData, monster_info, 0x01F2)
    CHECK_STRUCT(pstClientData, monster_debug, 0x01F3)
    CHECK_STRUCT(pstClientData, item_rare, 0x01F7)
    CHECK_STRUCT(pstClientData, item_unique, 0x01F8)
    CHECK_STRUCT(pstClientData, act, 0x01FB)
    CHECK_STRUCT(pstClientData, no_preload, 0x01FF)
    CHECK_STRUCT(pstClientData, direct, 0x0200)
    CHECK_STRUCT(pstClientData, low_end, 0x0201)
    CHECK_STRUCT(pstClientData, no_gfx_compress, 0x0202)
    CHECK_STRUCT(pstClientData, arena, 0x0203)
    CHECK_STRUCT(pstClientData, txt, 0x0211)
    CHECK_STRUCT(pstClientData, log, 0x0212)
    CHECK_STRUCT(pstClientData, msg_log, 0x0213)
    CHECK_STRUCT(pstClientData, safe_mode, 0x0214)
    CHECK_STRUCT(pstClientData, no_save, 0x0215)
    CHECK_STRUCT(pstClientData, seed, 0x0216)
    CHECK_STRUCT(pstClientData, cheats, 0x021A)
    CHECK_STRUCT(pstClientData, teen, 0x021B)
    CHECK_STRUCT(pstClientData, no_sound, 0x021C)
    CHECK_STRUCT(pstClientData, quests, 0x021D)
    CHECK_STRUCT(pstClientData, build, 0x021F)
    CHECK_STRUCT(pstClientData, sound_background, 0x0220)
    CHECK_STRUCT(pstClientData, skip_to_bnet, 0x0359)
    printf("finish checking 1.13d\r\n");

    free(pstClientData);
    free(pstClientData109);
    free(pstClientData110);
    free(pstClientData111);
    free(pstClientData112);
}

int main(int argc, char** argv)
{
    //printf("%d %X\r\n", sizeof(ST_CLIENT_DATA), sizeof(ST_CLIENT_DATA));
    static ST_CLIENT_DATA stClientData;
    char acBuffer[MAX_PATH] = { 0 };
    ST_LAUNCH_CALLBACK *pstCallBack = NULL;
    DWORD dwGameMode = launcher;
    DWORD dwVideoMode;
    int iFixedAspectRatio;
    BOOL boolInitGfx = FALSE, boolInitSound = FALSE, boolLoadMpqs = FALSE;
    HANDLE hLanguageMPQ = NULL;
    DWORD i;
    int iVer1 = 0, iVer2 = 0;
    char cVer3 = 'x';
    HANDLE ahExtendMPQ[MAX_EXTEND_MPQ] = {NULL};
    HANDLE event;

    memset(&stClientData, 0, sizeof(stClientData));
    parse_command(argc, argv, &stClientData);

    if ( TRUE == m_boolCheckStruct )
    {
        D2Loader_CheckStruct();
        return 0;
    }

    if ( 0 > m_iGameVersion )
    {
        m_iGameVersion = GetD2Version("Game.exe");
    }
    if ( V109d != m_iGameVersion && V110f != m_iGameVersion && V111b != m_iGameVersion && V112a != m_iGameVersion && V113c != m_iGameVersion && V113d != m_iGameVersion )
    {
        msgBox(m_pcBoxName, MB_OK | MB_ICONEXCLAMATION,
            "Current version of LoD (%s) isn't compatible with D2Loader.\n\n"
            "Please, install a patch between 1.09d/1.10f/1.11b/1.12a/1.13c/1.13d.",
            GetVersionString(m_iGameVersion));
        return 0;
    }

    event = OpenEventA(2, D2TRUE, "DIABLO_II_OK");
    if ( event )
    {
        SetEvent(event);
        CloseHandle(event);
    }

    D2Loader_InitClientData(&stClientData);

    LoadAllLibraries();
    if ( TRUE != D2Loader_InitFuncPtr() )
    {
        return 0;
    }

    D2Loader_PatchMpqPath();
    D2Loader_PatchDepFix();
    D2Loader_PatchMultiOpen();

    stClientData.mpq_callback = Proc_new;

    if ( stClientData.glide_mode )
    {
        dwVideoMode = glide;
    }
    else if ( stClientData.window_mode )
    {
        dwVideoMode = gdi;
    }
    else
    {
        dwVideoMode = (stClientData.d3d_mode ? d3d : ddraw);
    }

#if 0
    //以当前目录为准，忽略注册表
    memset(acBuffer, 0, sizeof(acBuffer));
    m_pfnStormRegLoadString("Diablo II", "InstallPath", 0, acBuffer, sizeof(acBuffer) / sizeof(acBuffer[0]));
    SetCurrentDirectoryA(acBuffer);
#endif

    memset(acBuffer, 0, sizeof(acBuffer));

    switch ( m_iGameVersion )
    {
        case V109d:
            iVer1 = 1;
            iVer2 = 9;
            cVer3 = 'd';
            break;

        case V110f:
            iVer1 = 1;
            iVer2 = 10;
            cVer3 = 'f';
            break;

        case V111b:
            iVer1 = 1;
            iVer2 = 11;
            cVer3 = 'b';
            break;

        case V112a:
            iVer1 = 1;
            iVer2 = 12;
            cVer3 = 'a';
            break;

        case V113c:
            iVer1 = 1;
            iVer2 = 13;
            cVer3 = 'c';
            break;

        case V113d:
            iVer1 = 1;
            iVer2 = 13;
            cVer3 = 'd';
            break;

        default:
            break;

    }
    if ( 0 >= m_pfnStormSprintf(acBuffer, sizeof(acBuffer) / sizeof(acBuffer[0]), "v%d.%02d", iVer1, iVer2) )
    {
        msgBox(m_pcBoxName, MB_OK | MB_ICONEXCLAMATION, "Call Storm.Sprintf failed!");
        goto out;
    }

    m_pfnFogSetLogPrefix("D2");
    m_pfnFogSetErrorHandler("Diablo II", D2Critical_Callback, acBuffer, D2TRUE);

    if ( NULL != m_pfnD2Common10097 )
    {
        m_pfnD2Common10097();
    }

    m_pfnFogSetFileOptions(stClientData.direct, D2FALSE);
    m_pfnFogSetAsyncData(D2TRUE, D2FALSE);
    m_pfnD2Fog10082();
    m_pfnFogInit();
    boolInitGfx = TRUE;

    printf("Loading MPQs...\r\n");
    if ( !m_pfnD2WinLoadMPQs() )
    {
        msgBox(m_pcBoxName, MB_OK | MB_ICONEXCLAMATION, "Load MPQs failed!");
        goto out;
    }
    boolLoadMpqs = TRUE;
    D2Loader_PutClientData(&stClientData);
    if ( !m_pfnD2WinLoadExpansionMPQs(NULL, NULL, 0, m_pvClientData) )
    {
        msgBox(m_pcBoxName, MB_OK | MB_ICONEXCLAMATION, "Load Expansion MPQs failed!");
        goto out;
    }
    D2Loader_GetClientData(&stClientData);
    stClientData.expansion = m_pfnFogIsExpansion();

    if ( 0 != m_acLanguageMpq[0] && !m_pfnStormOpenArchive(m_acLanguageMpq, 0x1B58, 2, &hLanguageMPQ) )
    {
        msgBox(m_pcBoxName, MB_OK | MB_ICONEXCLAMATION, "Load Language MPQ failed!");
        goto out;
    }

    for ( i = 0; i < m_dwExtendMpq; ++i )
    {
        if ( !m_pfnStormOpenArchive(m_aacExtendMpq[i], 0x1B58, 2, &ahExtendMPQ[i]) )
        {
            msgBox(m_pcBoxName, MB_OK | MB_ICONEXCLAMATION, "Load MPQ %s failed!", m_aacExtendMpq[i]);
            goto out;
        }
    }

    printf("Initing GFX...\r\n");
    if ( !m_pfnD2WinInitGfx(GetModuleHandle(NULL), dwVideoMode, stClientData.window_mode, !stClientData.no_gfx_compress) )
    {
        msgBox(m_pcBoxName, MB_OK | MB_ICONEXCLAMATION, "Init GFX failed!");
        goto out;
    }

    if ( stClientData.perspective && dwVideoMode >= glide )
    {
        m_pfnD2GfxSetPerspective(D2TRUE);
    }

    printf("Creating a window...\r\n");
    if ( !m_pfnD2WinCreateWindow(stClientData.window_mode, res_800x600) )
    {
        msgBox(m_pcBoxName, MB_OK | MB_ICONEXCLAMATION, "Create window failed!");
        goto out;
    }

    if ( 0 != m_acGameTitle[0] )
    {
        if ( !_stricmp("notitle", m_acGameTitle) )
        {
            SetWindowText(m_pfnGetHwnd(), "");
        }
        else
        {
            SetWindowText(m_pfnGetHwnd(), m_acGameTitle);
        }
    }

    if ( TRUE == m_boolNoBorder )
    {
        HWND hWnd = m_pfnGetHwnd();
        SetWindowLong(hWnd, GWL_STYLE, (GetWindowLong(hWnd, GWL_STYLE) | WS_POPUP) & ~WS_CAPTION); 
    }

    if ( stClientData.low_quality )
    {
        m_pfnD2GfxSetLowQuality();
    }
    if ( stClientData.gamma )
    {
        m_pfnD2GfxSetGamma(stClientData.gamma);
    }
    iFixedAspectRatio = 1;
    if ( NULL != m_pfnStormRegLoadValue )
    {
        m_pfnStormRegLoadValue("Diablo II", "Fixed Aspect Ratio", 0, &iFixedAspectRatio);
    }
    if ( NULL != m_pfnD2GfxFixAspectRatio && (stClientData.fix_aspect_ratio || iFixedAspectRatio != 1) )
    {
        m_pfnD2GfxFixAspectRatio();
    }
    if ( !stClientData.expansion )
    {
        m_pfnStormSetResolution("Diablo II", "Resolution", 0, 0);
    }

    if ( !stClientData.no_sound )
    {
        printf("Initing sound system...\r\n");
        m_pfnD2SoundInit(stClientData.expansion, D2FALSE);
        boolInitSound = TRUE;
    }

    for ( i = 0; i < m_dwExtendPlugin; ++i )
    {
        D2Loader_LoadLibrary(m_aacExtendPlugin[i]);
    }

    switch ( m_iGameVersion )
    {
        case V109d:
        case V110f:
        case V111b:
        case V112a:
        case V113c:
        case V113d:
            m_pfnD2LaunchGetCb = (type_pfnD2LaunchGetCb)GetDllOffset2("D2Launch.dll", "QueryInterface");
            m_pfnD2ClientGetCb = (type_pfnD2LaunchGetCb)GetDllOffset2("D2Client.dll", "QueryInterface");
            m_pfnD2MultiGetCb = (type_pfnD2LaunchGetCb)GetDllOffset2("D2Multi.dll", "QueryInterface");
            break;

        default:
            break;
    }

    D2Loader_PutClientData(&stClientData);
    while ( TRUE )
    {
        printf("Entering the game mode: %d\r\n", dwGameMode);

        switch ( dwGameMode )
        {
            case launcher:
                pstCallBack = m_pfnD2LaunchGetCb();
                break;

            case client:
                pstCallBack = m_pfnD2ClientGetCb();
                break;

            case multiplayer:
                pstCallBack = m_pfnD2MultiGetCb();
                break;

            default:
                pstCallBack = NULL;
                break;
        }

        if ( NULL == pstCallBack )
        {
            break;
        }
        dwGameMode = pstCallBack->pfnLaunch(m_pvClientData);
    }

out:
    if ( !stClientData.no_sound && boolInitSound )
    {
        printf("Shutting down the sound system...\r\n");
        m_pfnD2SoundShutdown();
    }
    if ( boolInitGfx )
    {
        printf("Shutting down the GFX...\r\n");
        m_pfnD2WinDeinitGFX();
        m_pfnD2GfxRelease();
    }
    m_pfnD2WinUninit();
    m_pfnFogFreeAsyncData();
    m_pfnD2MCPClientUninit();
#if 0
    v14 = *(_DWORD *)(v3 + 545);
    if ( v14 )
    {
        (*(void (**)(void))(v14 + 16))();
    }
#endif
    if ( NULL != m_pfnD2Common10925 )
    {
        m_pfnD2Common10925();
    }
    m_pfnFreePools(NULL);
    if ( hLanguageMPQ )
    {
        m_pfnStormCloseArchive(hLanguageMPQ);
    }
    for ( i = 0; i < m_dwExtendMpq; ++i )
    {
        if ( ahExtendMPQ[i] )
        {
            m_pfnStormCloseArchive(ahExtendMPQ[i]);
        }
    }
    if ( boolLoadMpqs && NULL != m_pfnD2WinUnloadMPQs )
    {
        printf("Unloading the MPQs...\r\n");
        m_pfnD2WinUnloadMPQs();
    }

    D2Loader_FreeClientData(&stClientData);
    printf("quit game!\r\n");

    return 0;
}

